@vibecontrols/agent 2026.531.8 → 2026.531.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-53e9wvdc.js → app-zcb5a9pn.js} +1 -1
- package/dist/cli.js +1 -1
- package/dist/{index-k904mt6w.js → index-ftx08bfa.js} +1 -1
- package/dist/{index-zww580wx.js → index-g9h64dn9.js} +1 -1
- package/dist/{index-vvp9h4hs.js → index-h6a1kkfk.js} +2 -2
- package/dist/{index-dj932gfv.js → index-her974n1.js} +2 -2
- package/dist/{index-a5ta2sm5.js → index-n3y918j2.js} +1 -1
- package/dist/{index-dwj2yb6c.js → index-pne5682j.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{plugin-system-vv1y4xm1.js → plugin-system-5myz4wgw.js} +1 -1
- package/dist/{secondary-profile-attach-2wnm7ar1.js → secondary-profile-attach-k49k65x7.js} +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{createApp}from"./index-
|
|
2
|
+
import{createApp}from"./index-her974n1.js";import"./index-926vt87h.js";import"./index-g9h64dn9.js";import"./index-skmkfyzb.js";import"./index-95nmejfd.js";import"./index-34syaqa8.js";import"./index-y5q0m3cx.js";import"./index-n3y918j2.js";import"./index-h6a1kkfk.js";import"./index-srbb2214.js";import"./index-sfjbh6fa.js";import"./index-3jez1q8j.js";import"./index-brtw3j8x.js";import"./index-b4wy3jrt.js";import"./index-d3mz9vws.js";import"./index-rnk0kny8.js";import"./index-2pqv0bya.js";import"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{createApp};
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{pruneStaleBunBinShims}from"./index-6xb3dqrh.js";import{listInstalledPrereqs}from"./index-23rdsqea.js";import{runAliasInherit}from"./index-9e1fgxea.js";import{checkDependencies,installDependencies}from"./index-fg47j98r.js";import{clearTunnelState,getBootstrapTunnelUrl,isProcessAlive,listPersistedTunnels,readTunnelState}from"./index-95nmejfd.js";import{ServiceManager}from"./index-34syaqa8.js";import{REDACTED_VALUE,redactUnknownSecrets}from"./index-y5q0m3cx.js";import{CliContributorRegistry,createIframeBridge,getActiveContributorRegistry,setActiveContributorRegistry}from"./index-a5ta2sm5.js";import{ABILITIES,PLUGIN_CATALOG,PluginManager,ServiceRegistry,collectPluginBinaries,getDefaultPluginsForAbilities,resolvePluginByShortName}from"./index-vvp9h4hs.js";import{getOsAdapter}from"./index-srbb2214.js";import{telemetryService}from"./index-sfjbh6fa.js";import{validateEvent}from"./index-3jez1q8j.js";import{loadAgentApiKey}from"./index-b4wy3jrt.js";import"./index-d3mz9vws.js";import"./index-rnk0kny8.js";import{Command}from"./index-qbvtba0e.js";import{apiDelete,apiGet,apiPost,apiPut,getAgentUrl,isInteractive,maybePrintJson,pickOutputMode,promptConfirm,resolveApiKey,resolveProfileName,runMultimode}from"./index-wvabz3xh.js";import{blank,colors,errMsg,fail,formatStatus,formatTable,header,icons,info,kv,printAgentDetails,setColorsEnabled,shortId,success,timeAgo,warn}from"./index-r0qezdp7.js";import{interactiveDetail,interactiveTable,stripAnsi}from"./index-campp0wv.js";import{BUILTIN_PROFILE_SEEDS,NAMED_ENV_PRESETS,applyAgentConfigToEnv,assertValidProfileName,buildProfileSeed,clearAllSecrets,deleteProfile,ensureProfilesInitialized,getAgentConfigDefaults,getAgentConfigPath,getProfilePath,getSecret,listProfileNames,previewClearAllSecrets,readActiveProfileName,readAgentConfig,readProfile,writeActiveProfileName,writeAgentConfig,writeProfile}from"./index-2pqv0bya.js";import{assertAllowedGatewayUrl,gatewayClient,getDaemonProfile}from"./index-7pdmqbj8.js";import{getPluginRegistry,getProfileDataDir,getVibecontrolsDir,getVibecontrolsProductDir,getVibecontrolsProfile,getVibecontrolsProfilesDir}from"./index-bysm7taq.js";import"./index-zs58d1hc.js";import{__require}from"./index-e9rt4m94.js";import{join as join14}from"path";import{existsSync as existsSync2}from"fs";import{join as join2}from"path";import{createServer}from"net";function isPortAvailable(port,host="0.0.0.0"){return new Promise((resolve)=>{let server=createServer();server.once("error",()=>resolve(!1)),server.once("listening",()=>{server.close(()=>resolve(!0))}),server.listen({host,port,exclusive:!0})})}async function findFreePortFrom(startPort,host="0.0.0.0"){let port=startPort;while(!await isPortAvailable(port,host))if(port++,port>65535)throw Error(`No free port found starting from ${startPort}`);return port}import{existsSync,readFileSync,unlinkSync,writeFileSync}from"fs";import{join}from"path";var CONSENT_FILENAME=".consent.json";function readConsentAbilities(productDir){try{let raw=readFileSync(consentPath(productDir),"utf8"),rec=JSON.parse(raw);if(Array.isArray(rec.abilities)&&rec.abilities.length>0)return new Set(rec.abilities)}catch{}return null}function consentPath(productDir){return join(productDir,CONSENT_FILENAME)}function hasConsent(productDir){return existsSync(consentPath(productDir))}function getPlannedInstall(options,selectedAbilities){let abilities=selectedAbilities??new Set(ABILITIES.filter((a)=>a.defaultSelected||a.mandatory).map((a)=>a.key)),plugins=options.skipPluginInstall?[]:getDefaultPluginsForAbilities(process.platform,abilities).filter((p)=>!options.skipPlugins.includes(p.packageName)&&!options.skipPlugins.includes(p.pluginName)).map((p)=>({packageName:p.packageName,pluginName:p.pluginName,reason:p.description}));return{tools:[],plugins}}async function promptAbilitySelection(){let defaults=new Set(ABILITIES.filter((a)=>a.defaultSelected||a.mandatory).map((a)=>a.key));for(let a of ABILITIES)if(a.mandatory)defaults.add(a.key);if(!(Boolean(process.stdin.isTTY)&&process.env.VIBE_AUTO_ACCEPT!=="1"))return defaults;try{let{interactiveCheckboxes}=await import("./interactive-v8zwz6wf.js"),result=await interactiveCheckboxes("Select the agent abilities to install",ABILITIES.map((a)=>({key:a.key,label:a.label,description:a.description,mandatory:a.mandatory})),defaults);if(result){for(let a of ABILITIES)if(a.mandatory)result.add(a.key);return result}return defaults}catch{return defaults}}function parseAbilityKeys(csv){let valid=new Set(ABILITIES.map((a)=>a.key)),out=new Set;for(let tok of csv.split(",").map((s)=>s.trim().toLowerCase()))if(valid.has(tok))out.add(tok);for(let a of ABILITIES)if(a.mandatory)out.add(a.key);return out}function defaultAbilityKeys(){return new Set(ABILITIES.filter((a)=>a.defaultSelected||a.mandatory).map((a)=>a.key))}function persistConsent(productDir,plan,selectedAbilities,agentVersion){let record={acceptedAt:new Date().toISOString(),agentVersion,abilities:[...selectedAbilities],tools:plan.tools.map((t)=>({name:t.name,reason:t.reason})),plugins:plan.plugins.map((p)=>({packageName:p.packageName,reason:p.reason}))};writeFileSync(consentPath(productDir),JSON.stringify(record,null,2))}function register(program){program.command("start").description("Start a VibeControls agent instance").option("-p, --port <port>","Port to listen on. Default: 3005 for the 'default' profile, deterministic 3100-3399 for any other profile (FNV-1a hash) so multiple profiles run side-by-side without EADDRINUSE.").option("-n, --name <name>","Agent instance name","default").option("-f, --foreground","Run in foreground mode (default: daemon)",!1).option("--db-path <path>","Path to the agent storage directory").option("--profile <profile>","Profile name for isolated config (env: VIBECONTROLS_PROFILE)").option("--host <host>","Host to bind to","0.0.0.0").option("--skip-plugin-install","Skip automatic installation of default plugins",!1).option("--skip-plugin <name>","Skip a specific default plugin (repeatable). Accepts the short pluginName or the full package name.",(value,prev=[])=>[...prev,value],[]).option("--no-install-deps","Skip auto-installing missing tools (installed by their plugins on start by default)").option("-y, --yes","Skip the first-run install consent prompt. Equivalent to VIBE_AUTO_ACCEPT=1; useful for CI / Docker / nohup contexts.",!1).option("--client-id <id>","Gateway OAuth client ID (env: VIBE_CLIENT_ID)").option("--client-secret <secret>","Gateway OAuth client secret (env: VIBE_CLIENT_SECRET)").option("--global-gateway-url <url>","Global gateway URL (env: VIBE_GLOBAL_GATEWAY_URL)").option("--workspace-gateway-url <url>","Workspace gateway URL (env: VIBE_WORKSPACE_GATEWAY_URL)").option("--workspace-id <id>","Workspace ID for auto-report (env: VIBE_WORKSPACE_ID)").option("--isolate <plugins>","Comma-separated list of plugin names to force into worker_threads isolation (Phase 4 hostile-plugin mode). Sets VIBE_ISOLATE_PLUGINS in the daemon env.").option("--profiles <names>","Comma-separated list of profiles to host in this daemon (multi-profile boot). When unset, the daemon hosts a single profile resolved via --profile / VIBECONTROLS_PROFILE / 'default'. Sets VIBECONTROLS_PROFILES in the daemon env so app boot can spin up additional ProfileContexts via the in-process registry.").option("--strict-key-fetch","Refuse to boot if any profile's encryption-key fetch fails. Default lenient: a failed profile is marked degraded and the daemon stays up. Sets VIBE_STRICT_KEY_FETCH=1 in the daemon env.",!1).option("--json","Emit JSON").option("--plain","Plain text output (no interactive TUI). Implied for non-TTY / CI.").option("--abilities <list>","Comma-separated ability keys to install non-interactively (e.g. session,ai,gitops). Storage/tunnel/backup are always included.").option("--wait-ready [seconds]","Block until the agent is fully ready (or fails). Polls /health/ready and renders phase transitions live. Without a value, defaults to 60s. Exits non-zero if the agent ends up degraded or times out. Useful for scripts/CI.").option("--no-wait","Skip the phase tracker entirely after spawning the daemon. Restores the old fire-and-print behavior \u2014 for users who want the previous quick-return UX.").action(async function(opts){let merged={...program.opts(),...opts};try{let defaultPortForProfile=function(profileName){if(profileName==="default")return 3005;let h=2166136261;for(let i=0;i<profileName.length;i++)h=Math.imul(h^profileName.charCodeAt(i),16777619);return 3100+Math.abs(h)%300},serviceManager=new ServiceManager,profile=opts.profile||process.env.VIBECONTROLS_PROFILE||"default";if(process.env.VIBECONTROLS_PROFILE=profile,typeof opts.isolate==="string"&&opts.isolate.length>0)process.env.VIBE_ISOLATE_PLUGINS=opts.isolate;if(typeof opts.profiles==="string"&&opts.profiles.length>0){let names=opts.profiles.split(",").map((s)=>s.trim()).filter(Boolean);if(names.length>0){if(process.env.VIBECONTROLS_PROFILES=names.join(","),process.env.VIBECONTROLS_PROFILE=names[0],names.length>1)warn(`--profiles received ${names.length} entries; daemon boots with '${names[0]}'. Remaining (${names.slice(1).join(", ")}) will be attached via /api/profiles after listen.`)}}if(opts.strictKeyFetch===!0)process.env.VIBE_STRICT_KEY_FETCH="1";applyAgentConfigToEnv();let dbPath=opts.dbPath||getProfileDataDir(),productDir=getVibecontrolsProductDir(),firstRun=!existsSync2(dbPath)&&!existsSync2(join2(productDir,"agents.json")),outputMode=pickOutputMode(merged),canPromptInteractively=outputMode!=="json"&&outputMode!=="plain"&&isInteractive()&&opts.yes!==!0,selectedAbilities;if(typeof merged.abilities==="string"&&merged.abilities.trim()!=="")selectedAbilities=parseAbilityKeys(merged.abilities);else if(hasConsent(productDir))selectedAbilities=readConsentAbilities(productDir)??defaultAbilityKeys();else if(canPromptInteractively)selectedAbilities=await promptAbilitySelection();else selectedAbilities=defaultAbilityKeys();if(!hasConsent(productDir)){let plan=getPlannedInstall({installDeps:opts.installDeps===!0,skipPluginInstall:opts.skipPluginInstall===!0,skipPlugins:Array.isArray(opts.skipPlugin)?opts.skipPlugin:[]},selectedAbilities);if(firstRun&&plan.plugins.length>0)blank(),info(`Installing ${plan.plugins.length} plugin(s) for the selected abilities.`),blank();persistConsent(productDir,plan,selectedAbilities,process.env.VIBE_AGENT_VERSION)}if(firstRun){info("First run detected \u2014 running setup..."),blank();let skipPlugins=Array.isArray(opts.skipPlugin)?opts.skipPlugin:[],plannedPlugins=opts.skipPluginInstall?[]:getDefaultPluginsForAbilities(process.platform,selectedAbilities).filter((p)=>!skipPlugins.includes(p.packageName)&&!skipPlugins.includes(p.pluginName)).map((p)=>p.packageName),pluginBinaries=collectPluginBinaries(plannedPlugins),missing=checkDependencies(pluginBinaries).filter((d)=>!d.installed);if(missing.length>0)if(warn(`Missing tools: ${missing.map((d)=>d.name).join(", ")}`),opts.installDeps)info("Their plugins will install them automatically on startup.");else info("Auto-install skipped (`--no-install-deps`) \u2014 install them yourself or drop the flag.");else success("All plugin tools present.");blank()}if(!opts.skipPluginInstall){let skipPlugins=Array.isArray(opts.skipPlugin)?opts.skipPlugin:[],pluginManager=new PluginManager;await pluginManager.loadCorePlugins(),await pluginManager.loadAll();let installed=await pluginManager.ensureDefaultPlugins((message)=>{if(message.startsWith("No "))warn(message);else if(message.startsWith("Installed "))success(message);else if(message.startsWith("Failed "))warn(message);else info(message)},skipPlugins,selectedAbilities);if(installed.length>0)success(`Auto-installed ${installed.length} default plugin${installed.length>1?"s":""}.`),blank()}let resolvedProfile=opts.profile||process.env.VIBECONTROLS_PROFILE||opts.name||"default",port=opts.port?parseInt(opts.port,10):defaultPortForProfile(resolvedProfile);if(isNaN(port)||port<1||port>65535)fail("Invalid port number. Must be between 1 and 65535.");let availablePort=await findFreePortFrom(port);if(availablePort!==port)warn(`Port ${port} is in use. Auto-selecting port ${colors.bold(String(availablePort))} instead.`),port=availablePort;let host=opts.host,agentUrl=`http://${host==="0.0.0.0"?"localhost":host}:${port}`,clientId=opts.clientId||process.env.VIBE_CLIENT_ID,clientSecret=opts.clientSecret||process.env.VIBE_CLIENT_SECRET,globalGatewayUrl=opts.globalGatewayUrl||process.env.VIBE_GLOBAL_GATEWAY_URL,workspaceGatewayUrl=opts.workspaceGatewayUrl||process.env.VIBE_WORKSPACE_GATEWAY_URL,workspaceId=opts.workspaceId||process.env.VIBE_WORKSPACE_ID;if(clientId&&!clientSecret)fail("--client-secret is required when --client-id is provided.");if(clientSecret&&!clientId)fail("--client-id is required when --client-secret is provided.");if(clientId&&clientSecret){if(!globalGatewayUrl&&!process.env.VIBE_GLOBAL_GATEWAY_URL)fail("--global-gateway-url is required when providing client credentials (none set in env).");if(process.env.VIBE_CLIENT_ID=clientId,process.env.VIBE_CLIENT_SECRET=clientSecret,globalGatewayUrl)process.env.VIBE_GLOBAL_GATEWAY_URL=globalGatewayUrl;let{writeAgentConfig:writeAgentConfig2}=await import("./agent-config-y63ddxgy.js");if(writeAgentConfig2({clientId,clientSecret,workspaceId,globalGatewayUrl,workspaceGatewayUrl}),workspaceGatewayUrl)process.env.VIBE_WORKSPACE_GATEWAY_URL=workspaceGatewayUrl;if(workspaceId)process.env.VIBE_WORKSPACE_ID=workspaceId;success("Gateway credentials staged in env. Agent will authenticate and persist them on startup."),blank()}else if(workspaceGatewayUrl||workspaceId||globalGatewayUrl){if(warn("Workspace options provided without --client-id / --client-secret \u2014 they will be staged in env but unused until credentials are configured."),workspaceGatewayUrl)process.env.VIBE_WORKSPACE_GATEWAY_URL=workspaceGatewayUrl;if(workspaceId)process.env.VIBE_WORKSPACE_ID=workspaceId;if(globalGatewayUrl)process.env.VIBE_GLOBAL_GATEWAY_URL=globalGatewayUrl;blank()}if(opts.foreground){if(!merged.json)header("Starting agent in foreground"),kv("Name",opts.name),kv("Port",String(port)),kv("Host",host),kv("Database",dbPath),blank();let{bootstrapWorkspace}=await import("./bootstrap-workspace-qwed8tfg.js");bootstrapWorkspace();let{createApp}=await import("./app-53e9wvdc.js"),appInstance=await createApp({port,host,dbPath});if(await appInstance.start(),clientId&&clientSecret&&workspaceId&&globalGatewayUrl&&workspaceGatewayUrl){let result=await appInstance.finalize({clientId,clientSecret,workspaceId,globalGatewayUrl,workspaceGatewayUrl});if(!result.ok)warn(`Finalize failed \u2014 agent remains in awaiting-config state: ${result.error}`)}if(maybePrintJson(merged,{ok:!0,name:opts.name,port,host,pid:process.pid,url:agentUrl}));else await printAgentDetails(agentUrl);let shutdown=async()=>{await appInstance.stop(),process.exit(0)};process.on("SIGTERM",shutdown),process.on("SIGINT",shutdown)}else{if(!merged.json)header("Starting agent in daemon mode"),kv("Name",opts.name),kv("Port",String(port)),kv("Host",host),kv("Database",dbPath),blank();if(opts.installDeps===!1)process.env.VIBE_SKIP_PREREQ_INSTALL="1";await serviceManager.startDaemon({port,name:opts.name,daemon:!0,dbPath,host,profile});let actualPort=(await serviceManager.getStatus(opts.name))?.port??port,actualAgentUrl=`http://${host==="0.0.0.0"?"localhost":host}:${actualPort}`,waitReady=opts.waitReady,noWait=opts.wait===!1,requestedBlocking=waitReady!==void 0;if(noWait&&requestedBlocking)warn("--wait-ready is ignored because --no-wait was also passed.");let blocking=!noWait&&requestedBlocking,shouldRunTracker=!noWait&&(blocking||!merged.json),timeoutMs=(()=>{let fromFlag=typeof waitReady==="string"?Number.parseInt(waitReady,10):NaN;if(Number.isInteger(fromFlag)&&fromFlag>0)return fromFlag*1000;return blocking?60000:25000})();if(merged.json){if(shouldRunTracker){let{trackAgentReady}=await import("./agent-ready-tracker-5yq97296.js"),result=await trackAgentReady(actualAgentUrl,{timeoutMs,verbose:!1});if(blocking&&(result.outcome==="degraded"||result.outcome==="timeout"))process.exitCode=1;maybePrintJson(merged,{ok:result.outcome==="ready"||result.outcome==="awaiting-config",name:opts.name,port:actualPort,host,url:actualAgentUrl,abilities:[...selectedAbilities],outcome:result.outcome,bootState:result.bootState,degradedReasons:result.degradedReasons,summary:result.summary})}else maybePrintJson(merged,{ok:!0,name:opts.name,port:actualPort,host,url:actualAgentUrl});return}if(await printAgentDetails(actualAgentUrl),shouldRunTracker){let{trackAgentReady}=await import("./agent-ready-tracker-5yq97296.js");blank();let result=await trackAgentReady(actualAgentUrl,{timeoutMs,verbose:!0});if(blocking&&(result.outcome==="degraded"||result.outcome==="timeout"))process.exitCode=1}}}catch(err){let message=err instanceof Error?errMsg(err):String(err);if(merged.json){maybePrintJson(merged,{ok:!1,error:message});return}fail(`Failed to start agent: ${message}`)}})}function register2(program){program.command("stop").description("Stop a running VibeControls agent instance").option("-n, --name <name>","Agent instance name","default").option("--all","Stop all running agent instances",!1).option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts};try{let serviceManager=new ServiceManager;if(opts.all){let instances=await serviceManager.listInstances();if(!instances||instances.length===0){if(maybePrintJson(merged,{ok:!0,stopped:[]}))return;header("Stopping all agent instances"),blank(),info(`${icons.info} No running agent instances found.`);return}let stoppedNames=[];if(!merged.json)header("Stopping all agent instances"),blank();for(let instance of instances)try{if(await serviceManager.stop(instance.name),stoppedNames.push(instance.name),!merged.json)success(`${icons.success} Stopped ${colors.bold(instance.name)}`)}catch{if(!merged.json)fail(`Failed to stop ${colors.bold(instance.name)}`)}if(maybePrintJson(merged,{ok:!0,stopped:stoppedNames,total:instances.length}))return;blank(),success(`${icons.success} Stopped ${stoppedNames.length}/${instances.length} instance(s).`)}else{if(await serviceManager.stop(opts.name),maybePrintJson(merged,{ok:!0,name:opts.name}))return;header(`Stopping agent: ${opts.name}`),blank(),success(`${icons.success} Agent ${colors.bold(opts.name)} stopped.`)}}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(`Failed to stop agent: ${errMsg(err)}`)}})}function register3(program){program.command("restart").description("Restart a running VibeControls agent instance").option("-n, --name <name>","Agent instance name","default").option("-p, --port <port>","Port to listen on","3005").option("--db-path <path>","Path to the agent storage directory","./agent-db").option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts};try{let serviceManager=new ServiceManager;if(!merged.json)header(`Restarting agent: ${opts.name}`),blank(),info(`${icons.info} Stopping agent ${colors.bold(opts.name)}...`);try{if(await serviceManager.stop(opts.name),!merged.json)success(`${icons.success} Agent stopped.`)}catch{if(!merged.json)info(`${icons.info} Agent was not running.`)}let port=parseInt(opts.port,10);if(isNaN(port)||port<1||port>65535){if(merged.json){maybePrintJson(merged,{ok:!1,error:"Invalid port number"});return}fail("Invalid port number. Must be between 1 and 65535.");return}let config={port,name:opts.name,daemon:!0,dbPath:opts.dbPath};if(!merged.json)blank(),info(`${icons.info} Starting agent ${colors.bold(opts.name)}...`);await serviceManager.startDaemon(config);let agentUrl=`http://localhost:${port}`;if(maybePrintJson(merged,{ok:!0,name:opts.name,port,url:agentUrl}))return;blank(),success(`${icons.success} Agent ${colors.bold(opts.name)} restarted.`),blank(),kv("Port",String(port)),kv("Database",opts.dbPath),blank(),printAgentDetails(agentUrl)}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(`Failed to restart agent: ${errMsg(err)}`)}})}import{existsSync as existsSync3,readFileSync as readFileSync2,unlinkSync as unlinkSync2}from"fs";import{join as join3}from"path";function readProfileRuntime(profileName){let runtimePath=join3(getVibecontrolsProfilesDir(),profileName,"runtime.json");if(!existsSync3(runtimePath))return null;try{let data=JSON.parse(readFileSync2(runtimePath,"utf8"));if(typeof data.pid!=="number")return null;return{...data,pid:data.pid,path:runtimePath}}catch{return null}}async function killAgentProfile(profileName){let rt=readProfileRuntime(profileName);if(!rt)return null;if(!isProcessAlive(rt.pid)){try{unlinkSync2(rt.path)}catch{}return`agent pid=${rt.pid} (${profileName}) already gone (cleaned runtime.json).`}let osAdapter=getOsAdapter();if(!osAdapter.killProcessTree(rt.pid,"SIGTERM"))return`failed to SIGTERM agent pid=${rt.pid}.`;if(await new Promise((r)=>setTimeout(r,3000)),isProcessAlive(rt.pid))osAdapter.killProcessTree(rt.pid,"SIGKILL");try{unlinkSync2(rt.path)}catch{}return`agent ${profileName} pid=${rt.pid}${rt.port?` (port ${rt.port})`:""} killed.`}async function killProfileTunnel(profileName){let dir=join3(getVibecontrolsProfilesDir(),profileName),state=readTunnelState(dir);if(!state)return null;if(!isProcessAlive(state.pid))return clearTunnelState(dir),`tunnel pid=${state.pid} already gone (cleaned state).`;let osAdapter=getOsAdapter();if(!osAdapter.killProcessTree(state.pid,"SIGTERM"))return`failed to SIGTERM tunnel pid=${state.pid}.`;if(await new Promise((r)=>setTimeout(r,3000)),isProcessAlive(state.pid))osAdapter.killProcessTree(state.pid,"SIGKILL");return clearTunnelState(dir),`tunnel ${state.provider} pid=${state.pid} (${state.url??"no url"}) killed.`}function register4(program){program.command("kill").description("Force-kill an agent instance and its tunnel. Defaults to active profile.").option("-n, --name <name>","Agent instance name (alias of --profile, kept for back-compat)").option("--profile <name>","Kill a specific profile (overrides --name and VIBECONTROLS_PROFILE)").option("--all","Kill every agent instance and every profile's tunnel",!1).option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts},killedAll=[];try{let serviceManager=new ServiceManager;if(opts.all){if(!merged.json)header("Force killing all agent instances + tunnels"),blank();let instances=await serviceManager.listInstances()??[],killedAgents=0,seenProfiles=new Set;for(let instance2 of instances)try{if(await serviceManager.kill(instance2.name),!merged.json)success(`${icons.success} agent ${colors.bold(instance2.name)} (pid=${instance2.pid||"?"}) killed.`);killedAll.push(instance2.name),killedAgents++,seenProfiles.add(instance2.name)}catch{if(!merged.json)warn(`${icons.warning} could not kill agent ${instance2.name}.`)}try{let fs=await import("fs"),agentsDir=getVibecontrolsProfilesDir(),entries=fs.existsSync(agentsDir)?fs.readdirSync(agentsDir):[];for(let profile of entries){if(seenProfiles.has(profile))continue;let rtResult=await killAgentProfile(profile);if(rtResult)success(`${icons.success} ${rtResult}`),killedAgents++}}catch(err){warn(`${icons.warning} runtime.json sweep failed: ${err instanceof Error?errMsg(err):String(err)}`)}let tunnels=listPersistedTunnels({allProfiles:!0}),killedTunnels=0;for(let t of tunnels){if(!t.alive){clearTunnelState(t.profileDir);continue}let osAdapter=getOsAdapter();if(osAdapter.killProcessTree(t.pid,"SIGTERM"),await new Promise((r)=>setTimeout(r,3000)),isProcessAlive(t.pid))osAdapter.killProcessTree(t.pid,"SIGKILL");clearTunnelState(t.profileDir),success(`${icons.success} tunnel ${t.provider} pid=${t.pid} (${t.url??"no url"}, ${t.profileDir.split("/").pop()}) killed.`),killedTunnels++}if(maybePrintJson(merged,{ok:!0,killed:killedAll,tunnels:killedTunnels}))return;blank(),success(`${icons.success} killed ${killedAgents} agent(s) and ${killedTunnels} tunnel(s).`);return}let profileName=opts.profile||opts.name||getVibecontrolsProfile();if(!merged.json)header(`Force killing profile: ${profileName}`),blank();let instance=await serviceManager.getStatus(profileName);if(instance){if(await serviceManager.kill(profileName),killedAll.push(profileName),!merged.json)success(`${icons.success} agent ${colors.bold(profileName)} pid=${instance.pid||"?"} killed.`)}else{let rtResult=await killAgentProfile(profileName);if(rtResult){if(killedAll.push(profileName),!merged.json)success(`${icons.success} ${rtResult}`)}else if(!merged.json)info(`${icons.info} no running agent service for profile ${profileName}.`)}let tunnelResult=await killProfileTunnel(profileName);if(tunnelResult){if(!merged.json)success(`${icons.success} ${tunnelResult}`)}else if(!merged.json)info(`${icons.info} no tunnel recorded for profile ${profileName}.`);maybePrintJson(merged,{ok:!0,killed:killedAll})}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(`Failed to kill: ${errMsg(err)}`)}})}function register5(program){program.command("list").alias("ls").description("List all VibeControls agent instances").option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(opts){try{let merged={...program.opts(),...opts},sm=new ServiceManager;await runMultimode({mode:pickOutputMode(merged),fetchData:()=>sm.listInstances(),plain:(instances)=>{if(header("Agent Instances"),blank(),!instances||instances.length===0){info(`${icons.info} No agent instances found.`),info(`Run ${colors.bold("vibe start")} to start an agent.`);return}let rows=instances.map((inst)=>({Name:inst.name,Status:formatStatus(inst.status),PID:inst.pid||"-",Port:inst.port||"-",Host:inst.config?.host||"-",Database:inst.config?.dbPath||"-",Started:inst.startTime?timeAgo(inst.startTime):"-"}));formatTable(rows),blank();let running=instances.filter((i)=>i.status==="running").length,stopped=instances.length-running;info(`${icons.info} ${instances.length} instance(s): ${colors.green(String(running))} running, ${colors.dim(String(stopped))} stopped`)},interactive:async(instances)=>{if(!instances||instances.length===0){header("Agent Instances"),blank(),info(`${icons.info} No agent instances found.`),info(`Run ${colors.bold("vibe start")} to start an agent.`);return}let rows=instances.map((inst)=>{let lines=[`${colors.bold(inst.name)} ${formatStatus(inst.status)}`,"",` PID: ${inst.pid??"-"}`,` Port: ${inst.port??"-"}`,` Host: ${inst.config?.host??"-"}`,` Database: ${inst.config?.dbPath??"-"}`,` Started: ${inst.startTime?timeAgo(inst.startTime):"-"}`];return{id:inst.name,label:inst.name,hint:inst.status==="running"?"running":"stopped",detail:lines.join(`
|
|
3
|
+
import{pruneStaleBunBinShims}from"./index-6xb3dqrh.js";import{listInstalledPrereqs}from"./index-23rdsqea.js";import{runAliasInherit}from"./index-9e1fgxea.js";import{checkDependencies,installDependencies}from"./index-fg47j98r.js";import{clearTunnelState,getBootstrapTunnelUrl,isProcessAlive,listPersistedTunnels,readTunnelState}from"./index-95nmejfd.js";import{ServiceManager}from"./index-34syaqa8.js";import{REDACTED_VALUE,redactUnknownSecrets}from"./index-y5q0m3cx.js";import{CliContributorRegistry,createIframeBridge,getActiveContributorRegistry,setActiveContributorRegistry}from"./index-n3y918j2.js";import{ABILITIES,PLUGIN_CATALOG,PluginManager,ServiceRegistry,collectPluginBinaries,getDefaultPluginsForAbilities,resolvePluginByShortName}from"./index-h6a1kkfk.js";import{getOsAdapter}from"./index-srbb2214.js";import{telemetryService}from"./index-sfjbh6fa.js";import{validateEvent}from"./index-3jez1q8j.js";import{loadAgentApiKey}from"./index-b4wy3jrt.js";import"./index-d3mz9vws.js";import"./index-rnk0kny8.js";import{Command}from"./index-qbvtba0e.js";import{apiDelete,apiGet,apiPost,apiPut,getAgentUrl,isInteractive,maybePrintJson,pickOutputMode,promptConfirm,resolveApiKey,resolveProfileName,runMultimode}from"./index-wvabz3xh.js";import{blank,colors,errMsg,fail,formatStatus,formatTable,header,icons,info,kv,printAgentDetails,setColorsEnabled,shortId,success,timeAgo,warn}from"./index-r0qezdp7.js";import{interactiveDetail,interactiveTable,stripAnsi}from"./index-campp0wv.js";import{BUILTIN_PROFILE_SEEDS,NAMED_ENV_PRESETS,applyAgentConfigToEnv,assertValidProfileName,buildProfileSeed,clearAllSecrets,deleteProfile,ensureProfilesInitialized,getAgentConfigDefaults,getAgentConfigPath,getProfilePath,getSecret,listProfileNames,previewClearAllSecrets,readActiveProfileName,readAgentConfig,readProfile,writeActiveProfileName,writeAgentConfig,writeProfile}from"./index-2pqv0bya.js";import{assertAllowedGatewayUrl,gatewayClient,getDaemonProfile}from"./index-7pdmqbj8.js";import{getPluginRegistry,getProfileDataDir,getVibecontrolsDir,getVibecontrolsProductDir,getVibecontrolsProfile,getVibecontrolsProfilesDir}from"./index-bysm7taq.js";import"./index-zs58d1hc.js";import{__require}from"./index-e9rt4m94.js";import{join as join14}from"path";import{existsSync as existsSync2}from"fs";import{join as join2}from"path";import{createServer}from"net";function isPortAvailable(port,host="0.0.0.0"){return new Promise((resolve)=>{let server=createServer();server.once("error",()=>resolve(!1)),server.once("listening",()=>{server.close(()=>resolve(!0))}),server.listen({host,port,exclusive:!0})})}async function findFreePortFrom(startPort,host="0.0.0.0"){let port=startPort;while(!await isPortAvailable(port,host))if(port++,port>65535)throw Error(`No free port found starting from ${startPort}`);return port}import{existsSync,readFileSync,unlinkSync,writeFileSync}from"fs";import{join}from"path";var CONSENT_FILENAME=".consent.json";function readConsentAbilities(productDir){try{let raw=readFileSync(consentPath(productDir),"utf8"),rec=JSON.parse(raw);if(Array.isArray(rec.abilities)&&rec.abilities.length>0)return new Set(rec.abilities)}catch{}return null}function consentPath(productDir){return join(productDir,CONSENT_FILENAME)}function hasConsent(productDir){return existsSync(consentPath(productDir))}function getPlannedInstall(options,selectedAbilities){let abilities=selectedAbilities??new Set(ABILITIES.filter((a)=>a.defaultSelected||a.mandatory).map((a)=>a.key)),plugins=options.skipPluginInstall?[]:getDefaultPluginsForAbilities(process.platform,abilities).filter((p)=>!options.skipPlugins.includes(p.packageName)&&!options.skipPlugins.includes(p.pluginName)).map((p)=>({packageName:p.packageName,pluginName:p.pluginName,reason:p.description}));return{tools:[],plugins}}async function promptAbilitySelection(){let defaults=new Set(ABILITIES.filter((a)=>a.defaultSelected||a.mandatory).map((a)=>a.key));for(let a of ABILITIES)if(a.mandatory)defaults.add(a.key);if(!(Boolean(process.stdin.isTTY)&&process.env.VIBE_AUTO_ACCEPT!=="1"))return defaults;try{let{interactiveCheckboxes}=await import("./interactive-v8zwz6wf.js"),result=await interactiveCheckboxes("Select the agent abilities to install",ABILITIES.map((a)=>({key:a.key,label:a.label,description:a.description,mandatory:a.mandatory})),defaults);if(result){for(let a of ABILITIES)if(a.mandatory)result.add(a.key);return result}return defaults}catch{return defaults}}function parseAbilityKeys(csv){let valid=new Set(ABILITIES.map((a)=>a.key)),out=new Set;for(let tok of csv.split(",").map((s)=>s.trim().toLowerCase()))if(valid.has(tok))out.add(tok);for(let a of ABILITIES)if(a.mandatory)out.add(a.key);return out}function defaultAbilityKeys(){return new Set(ABILITIES.filter((a)=>a.defaultSelected||a.mandatory).map((a)=>a.key))}function persistConsent(productDir,plan,selectedAbilities,agentVersion){let record={acceptedAt:new Date().toISOString(),agentVersion,abilities:[...selectedAbilities],tools:plan.tools.map((t)=>({name:t.name,reason:t.reason})),plugins:plan.plugins.map((p)=>({packageName:p.packageName,reason:p.reason}))};writeFileSync(consentPath(productDir),JSON.stringify(record,null,2))}function register(program){program.command("start").description("Start a VibeControls agent instance").option("-p, --port <port>","Port to listen on. Default: 3005 for the 'default' profile, deterministic 3100-3399 for any other profile (FNV-1a hash) so multiple profiles run side-by-side without EADDRINUSE.").option("-n, --name <name>","Agent instance name","default").option("-f, --foreground","Run in foreground mode (default: daemon)",!1).option("--db-path <path>","Path to the agent storage directory").option("--profile <profile>","Profile name for isolated config (env: VIBECONTROLS_PROFILE)").option("--host <host>","Host to bind to","0.0.0.0").option("--skip-plugin-install","Skip automatic installation of default plugins",!1).option("--skip-plugin <name>","Skip a specific default plugin (repeatable). Accepts the short pluginName or the full package name.",(value,prev=[])=>[...prev,value],[]).option("--no-install-deps","Skip auto-installing missing tools (installed by their plugins on start by default)").option("-y, --yes","Skip the first-run install consent prompt. Equivalent to VIBE_AUTO_ACCEPT=1; useful for CI / Docker / nohup contexts.",!1).option("--client-id <id>","Gateway OAuth client ID (env: VIBE_CLIENT_ID)").option("--client-secret <secret>","Gateway OAuth client secret (env: VIBE_CLIENT_SECRET)").option("--global-gateway-url <url>","Global gateway URL (env: VIBE_GLOBAL_GATEWAY_URL)").option("--workspace-gateway-url <url>","Workspace gateway URL (env: VIBE_WORKSPACE_GATEWAY_URL)").option("--workspace-id <id>","Workspace ID for auto-report (env: VIBE_WORKSPACE_ID)").option("--isolate <plugins>","Comma-separated list of plugin names to force into worker_threads isolation (Phase 4 hostile-plugin mode). Sets VIBE_ISOLATE_PLUGINS in the daemon env.").option("--profiles <names>","Comma-separated list of profiles to host in this daemon (multi-profile boot). When unset, the daemon hosts a single profile resolved via --profile / VIBECONTROLS_PROFILE / 'default'. Sets VIBECONTROLS_PROFILES in the daemon env so app boot can spin up additional ProfileContexts via the in-process registry.").option("--strict-key-fetch","Refuse to boot if any profile's encryption-key fetch fails. Default lenient: a failed profile is marked degraded and the daemon stays up. Sets VIBE_STRICT_KEY_FETCH=1 in the daemon env.",!1).option("--json","Emit JSON").option("--plain","Plain text output (no interactive TUI). Implied for non-TTY / CI.").option("--abilities <list>","Comma-separated ability keys to install non-interactively (e.g. session,ai,gitops). Storage/tunnel/backup are always included.").option("--wait-ready [seconds]","Block until the agent is fully ready (or fails). Polls /health/ready and renders phase transitions live. Without a value, defaults to 60s. Exits non-zero if the agent ends up degraded or times out. Useful for scripts/CI.").option("--no-wait","Skip the phase tracker entirely after spawning the daemon. Restores the old fire-and-print behavior \u2014 for users who want the previous quick-return UX.").action(async function(opts){let merged={...program.opts(),...opts};try{let defaultPortForProfile=function(profileName){if(profileName==="default")return 3005;let h=2166136261;for(let i=0;i<profileName.length;i++)h=Math.imul(h^profileName.charCodeAt(i),16777619);return 3100+Math.abs(h)%300},serviceManager=new ServiceManager,profile=opts.profile||process.env.VIBECONTROLS_PROFILE||"default";if(process.env.VIBECONTROLS_PROFILE=profile,typeof opts.isolate==="string"&&opts.isolate.length>0)process.env.VIBE_ISOLATE_PLUGINS=opts.isolate;if(typeof opts.profiles==="string"&&opts.profiles.length>0){let names=opts.profiles.split(",").map((s)=>s.trim()).filter(Boolean);if(names.length>0){if(process.env.VIBECONTROLS_PROFILES=names.join(","),process.env.VIBECONTROLS_PROFILE=names[0],names.length>1)warn(`--profiles received ${names.length} entries; daemon boots with '${names[0]}'. Remaining (${names.slice(1).join(", ")}) will be attached via /api/profiles after listen.`)}}if(opts.strictKeyFetch===!0)process.env.VIBE_STRICT_KEY_FETCH="1";applyAgentConfigToEnv();let dbPath=opts.dbPath||getProfileDataDir(),productDir=getVibecontrolsProductDir(),firstRun=!existsSync2(dbPath)&&!existsSync2(join2(productDir,"agents.json")),outputMode=pickOutputMode(merged),canPromptInteractively=outputMode!=="json"&&outputMode!=="plain"&&isInteractive()&&opts.yes!==!0,selectedAbilities;if(typeof merged.abilities==="string"&&merged.abilities.trim()!=="")selectedAbilities=parseAbilityKeys(merged.abilities);else if(hasConsent(productDir))selectedAbilities=readConsentAbilities(productDir)??defaultAbilityKeys();else if(canPromptInteractively)selectedAbilities=await promptAbilitySelection();else selectedAbilities=defaultAbilityKeys();if(!hasConsent(productDir)){let plan=getPlannedInstall({installDeps:opts.installDeps===!0,skipPluginInstall:opts.skipPluginInstall===!0,skipPlugins:Array.isArray(opts.skipPlugin)?opts.skipPlugin:[]},selectedAbilities);if(firstRun&&plan.plugins.length>0)blank(),info(`Installing ${plan.plugins.length} plugin(s) for the selected abilities.`),blank();persistConsent(productDir,plan,selectedAbilities,process.env.VIBE_AGENT_VERSION)}if(firstRun){info("First run detected \u2014 running setup..."),blank();let skipPlugins=Array.isArray(opts.skipPlugin)?opts.skipPlugin:[],plannedPlugins=opts.skipPluginInstall?[]:getDefaultPluginsForAbilities(process.platform,selectedAbilities).filter((p)=>!skipPlugins.includes(p.packageName)&&!skipPlugins.includes(p.pluginName)).map((p)=>p.packageName),pluginBinaries=collectPluginBinaries(plannedPlugins),missing=checkDependencies(pluginBinaries).filter((d)=>!d.installed);if(missing.length>0)if(warn(`Missing tools: ${missing.map((d)=>d.name).join(", ")}`),opts.installDeps)info("Their plugins will install them automatically on startup.");else info("Auto-install skipped (`--no-install-deps`) \u2014 install them yourself or drop the flag.");else success("All plugin tools present.");blank()}if(!opts.skipPluginInstall){let skipPlugins=Array.isArray(opts.skipPlugin)?opts.skipPlugin:[],pluginManager=new PluginManager;await pluginManager.loadCorePlugins(),await pluginManager.loadAll();let installed=await pluginManager.ensureDefaultPlugins((message)=>{if(message.startsWith("No "))warn(message);else if(message.startsWith("Installed "))success(message);else if(message.startsWith("Failed "))warn(message);else info(message)},skipPlugins,selectedAbilities);if(installed.length>0)success(`Auto-installed ${installed.length} default plugin${installed.length>1?"s":""}.`),blank()}let resolvedProfile=opts.profile||process.env.VIBECONTROLS_PROFILE||opts.name||"default",port=opts.port?parseInt(opts.port,10):defaultPortForProfile(resolvedProfile);if(isNaN(port)||port<1||port>65535)fail("Invalid port number. Must be between 1 and 65535.");let availablePort=await findFreePortFrom(port);if(availablePort!==port)warn(`Port ${port} is in use. Auto-selecting port ${colors.bold(String(availablePort))} instead.`),port=availablePort;let host=opts.host,agentUrl=`http://${host==="0.0.0.0"?"localhost":host}:${port}`,clientId=opts.clientId||process.env.VIBE_CLIENT_ID,clientSecret=opts.clientSecret||process.env.VIBE_CLIENT_SECRET,globalGatewayUrl=opts.globalGatewayUrl||process.env.VIBE_GLOBAL_GATEWAY_URL,workspaceGatewayUrl=opts.workspaceGatewayUrl||process.env.VIBE_WORKSPACE_GATEWAY_URL,workspaceId=opts.workspaceId||process.env.VIBE_WORKSPACE_ID;if(clientId&&!clientSecret)fail("--client-secret is required when --client-id is provided.");if(clientSecret&&!clientId)fail("--client-id is required when --client-secret is provided.");if(clientId&&clientSecret){if(!globalGatewayUrl&&!process.env.VIBE_GLOBAL_GATEWAY_URL)fail("--global-gateway-url is required when providing client credentials (none set in env).");if(process.env.VIBE_CLIENT_ID=clientId,process.env.VIBE_CLIENT_SECRET=clientSecret,globalGatewayUrl)process.env.VIBE_GLOBAL_GATEWAY_URL=globalGatewayUrl;let{writeAgentConfig:writeAgentConfig2}=await import("./agent-config-y63ddxgy.js");if(writeAgentConfig2({clientId,clientSecret,workspaceId,globalGatewayUrl,workspaceGatewayUrl}),workspaceGatewayUrl)process.env.VIBE_WORKSPACE_GATEWAY_URL=workspaceGatewayUrl;if(workspaceId)process.env.VIBE_WORKSPACE_ID=workspaceId;success("Gateway credentials staged in env. Agent will authenticate and persist them on startup."),blank()}else if(workspaceGatewayUrl||workspaceId||globalGatewayUrl){if(warn("Workspace options provided without --client-id / --client-secret \u2014 they will be staged in env but unused until credentials are configured."),workspaceGatewayUrl)process.env.VIBE_WORKSPACE_GATEWAY_URL=workspaceGatewayUrl;if(workspaceId)process.env.VIBE_WORKSPACE_ID=workspaceId;if(globalGatewayUrl)process.env.VIBE_GLOBAL_GATEWAY_URL=globalGatewayUrl;blank()}if(opts.foreground){if(!merged.json)header("Starting agent in foreground"),kv("Name",opts.name),kv("Port",String(port)),kv("Host",host),kv("Database",dbPath),blank();let{bootstrapWorkspace}=await import("./bootstrap-workspace-qwed8tfg.js");bootstrapWorkspace();let{createApp}=await import("./app-zcb5a9pn.js"),appInstance=await createApp({port,host,dbPath});if(await appInstance.start(),clientId&&clientSecret&&workspaceId&&globalGatewayUrl&&workspaceGatewayUrl){let result=await appInstance.finalize({clientId,clientSecret,workspaceId,globalGatewayUrl,workspaceGatewayUrl});if(!result.ok)warn(`Finalize failed \u2014 agent remains in awaiting-config state: ${result.error}`)}if(maybePrintJson(merged,{ok:!0,name:opts.name,port,host,pid:process.pid,url:agentUrl}));else await printAgentDetails(agentUrl);let shutdown=async()=>{await appInstance.stop(),process.exit(0)};process.on("SIGTERM",shutdown),process.on("SIGINT",shutdown)}else{if(!merged.json)header("Starting agent in daemon mode"),kv("Name",opts.name),kv("Port",String(port)),kv("Host",host),kv("Database",dbPath),blank();if(opts.installDeps===!1)process.env.VIBE_SKIP_PREREQ_INSTALL="1";await serviceManager.startDaemon({port,name:opts.name,daemon:!0,dbPath,host,profile});let actualPort=(await serviceManager.getStatus(opts.name))?.port??port,actualAgentUrl=`http://${host==="0.0.0.0"?"localhost":host}:${actualPort}`,waitReady=opts.waitReady,noWait=opts.wait===!1,requestedBlocking=waitReady!==void 0;if(noWait&&requestedBlocking)warn("--wait-ready is ignored because --no-wait was also passed.");let blocking=!noWait&&requestedBlocking,shouldRunTracker=!noWait&&(blocking||!merged.json),timeoutMs=(()=>{let fromFlag=typeof waitReady==="string"?Number.parseInt(waitReady,10):NaN;if(Number.isInteger(fromFlag)&&fromFlag>0)return fromFlag*1000;return blocking?60000:25000})();if(merged.json){if(shouldRunTracker){let{trackAgentReady}=await import("./agent-ready-tracker-5yq97296.js"),result=await trackAgentReady(actualAgentUrl,{timeoutMs,verbose:!1});if(blocking&&(result.outcome==="degraded"||result.outcome==="timeout"))process.exitCode=1;maybePrintJson(merged,{ok:result.outcome==="ready"||result.outcome==="awaiting-config",name:opts.name,port:actualPort,host,url:actualAgentUrl,abilities:[...selectedAbilities],outcome:result.outcome,bootState:result.bootState,degradedReasons:result.degradedReasons,summary:result.summary})}else maybePrintJson(merged,{ok:!0,name:opts.name,port:actualPort,host,url:actualAgentUrl});return}if(await printAgentDetails(actualAgentUrl),shouldRunTracker){let{trackAgentReady}=await import("./agent-ready-tracker-5yq97296.js");blank();let result=await trackAgentReady(actualAgentUrl,{timeoutMs,verbose:!0});if(blocking&&(result.outcome==="degraded"||result.outcome==="timeout"))process.exitCode=1}}}catch(err){let message=err instanceof Error?errMsg(err):String(err);if(merged.json){maybePrintJson(merged,{ok:!1,error:message});return}fail(`Failed to start agent: ${message}`)}})}function register2(program){program.command("stop").description("Stop a running VibeControls agent instance").option("-n, --name <name>","Agent instance name","default").option("--all","Stop all running agent instances",!1).option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts};try{let serviceManager=new ServiceManager;if(opts.all){let instances=await serviceManager.listInstances();if(!instances||instances.length===0){if(maybePrintJson(merged,{ok:!0,stopped:[]}))return;header("Stopping all agent instances"),blank(),info(`${icons.info} No running agent instances found.`);return}let stoppedNames=[];if(!merged.json)header("Stopping all agent instances"),blank();for(let instance of instances)try{if(await serviceManager.stop(instance.name),stoppedNames.push(instance.name),!merged.json)success(`${icons.success} Stopped ${colors.bold(instance.name)}`)}catch{if(!merged.json)fail(`Failed to stop ${colors.bold(instance.name)}`)}if(maybePrintJson(merged,{ok:!0,stopped:stoppedNames,total:instances.length}))return;blank(),success(`${icons.success} Stopped ${stoppedNames.length}/${instances.length} instance(s).`)}else{if(await serviceManager.stop(opts.name),maybePrintJson(merged,{ok:!0,name:opts.name}))return;header(`Stopping agent: ${opts.name}`),blank(),success(`${icons.success} Agent ${colors.bold(opts.name)} stopped.`)}}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(`Failed to stop agent: ${errMsg(err)}`)}})}function register3(program){program.command("restart").description("Restart a running VibeControls agent instance").option("-n, --name <name>","Agent instance name","default").option("-p, --port <port>","Port to listen on","3005").option("--db-path <path>","Path to the agent storage directory","./agent-db").option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts};try{let serviceManager=new ServiceManager;if(!merged.json)header(`Restarting agent: ${opts.name}`),blank(),info(`${icons.info} Stopping agent ${colors.bold(opts.name)}...`);try{if(await serviceManager.stop(opts.name),!merged.json)success(`${icons.success} Agent stopped.`)}catch{if(!merged.json)info(`${icons.info} Agent was not running.`)}let port=parseInt(opts.port,10);if(isNaN(port)||port<1||port>65535){if(merged.json){maybePrintJson(merged,{ok:!1,error:"Invalid port number"});return}fail("Invalid port number. Must be between 1 and 65535.");return}let config={port,name:opts.name,daemon:!0,dbPath:opts.dbPath};if(!merged.json)blank(),info(`${icons.info} Starting agent ${colors.bold(opts.name)}...`);await serviceManager.startDaemon(config);let agentUrl=`http://localhost:${port}`;if(maybePrintJson(merged,{ok:!0,name:opts.name,port,url:agentUrl}))return;blank(),success(`${icons.success} Agent ${colors.bold(opts.name)} restarted.`),blank(),kv("Port",String(port)),kv("Database",opts.dbPath),blank(),printAgentDetails(agentUrl)}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(`Failed to restart agent: ${errMsg(err)}`)}})}import{existsSync as existsSync3,readFileSync as readFileSync2,unlinkSync as unlinkSync2}from"fs";import{join as join3}from"path";function readProfileRuntime(profileName){let runtimePath=join3(getVibecontrolsProfilesDir(),profileName,"runtime.json");if(!existsSync3(runtimePath))return null;try{let data=JSON.parse(readFileSync2(runtimePath,"utf8"));if(typeof data.pid!=="number")return null;return{...data,pid:data.pid,path:runtimePath}}catch{return null}}async function killAgentProfile(profileName){let rt=readProfileRuntime(profileName);if(!rt)return null;if(!isProcessAlive(rt.pid)){try{unlinkSync2(rt.path)}catch{}return`agent pid=${rt.pid} (${profileName}) already gone (cleaned runtime.json).`}let osAdapter=getOsAdapter();if(!osAdapter.killProcessTree(rt.pid,"SIGTERM"))return`failed to SIGTERM agent pid=${rt.pid}.`;if(await new Promise((r)=>setTimeout(r,3000)),isProcessAlive(rt.pid))osAdapter.killProcessTree(rt.pid,"SIGKILL");try{unlinkSync2(rt.path)}catch{}return`agent ${profileName} pid=${rt.pid}${rt.port?` (port ${rt.port})`:""} killed.`}async function killProfileTunnel(profileName){let dir=join3(getVibecontrolsProfilesDir(),profileName),state=readTunnelState(dir);if(!state)return null;if(!isProcessAlive(state.pid))return clearTunnelState(dir),`tunnel pid=${state.pid} already gone (cleaned state).`;let osAdapter=getOsAdapter();if(!osAdapter.killProcessTree(state.pid,"SIGTERM"))return`failed to SIGTERM tunnel pid=${state.pid}.`;if(await new Promise((r)=>setTimeout(r,3000)),isProcessAlive(state.pid))osAdapter.killProcessTree(state.pid,"SIGKILL");return clearTunnelState(dir),`tunnel ${state.provider} pid=${state.pid} (${state.url??"no url"}) killed.`}function register4(program){program.command("kill").description("Force-kill an agent instance and its tunnel. Defaults to active profile.").option("-n, --name <name>","Agent instance name (alias of --profile, kept for back-compat)").option("--profile <name>","Kill a specific profile (overrides --name and VIBECONTROLS_PROFILE)").option("--all","Kill every agent instance and every profile's tunnel",!1).option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts},killedAll=[];try{let serviceManager=new ServiceManager;if(opts.all){if(!merged.json)header("Force killing all agent instances + tunnels"),blank();let instances=await serviceManager.listInstances()??[],killedAgents=0,seenProfiles=new Set;for(let instance2 of instances)try{if(await serviceManager.kill(instance2.name),!merged.json)success(`${icons.success} agent ${colors.bold(instance2.name)} (pid=${instance2.pid||"?"}) killed.`);killedAll.push(instance2.name),killedAgents++,seenProfiles.add(instance2.name)}catch{if(!merged.json)warn(`${icons.warning} could not kill agent ${instance2.name}.`)}try{let fs=await import("fs"),agentsDir=getVibecontrolsProfilesDir(),entries=fs.existsSync(agentsDir)?fs.readdirSync(agentsDir):[];for(let profile of entries){if(seenProfiles.has(profile))continue;let rtResult=await killAgentProfile(profile);if(rtResult)success(`${icons.success} ${rtResult}`),killedAgents++}}catch(err){warn(`${icons.warning} runtime.json sweep failed: ${err instanceof Error?errMsg(err):String(err)}`)}let tunnels=listPersistedTunnels({allProfiles:!0}),killedTunnels=0;for(let t of tunnels){if(!t.alive){clearTunnelState(t.profileDir);continue}let osAdapter=getOsAdapter();if(osAdapter.killProcessTree(t.pid,"SIGTERM"),await new Promise((r)=>setTimeout(r,3000)),isProcessAlive(t.pid))osAdapter.killProcessTree(t.pid,"SIGKILL");clearTunnelState(t.profileDir),success(`${icons.success} tunnel ${t.provider} pid=${t.pid} (${t.url??"no url"}, ${t.profileDir.split("/").pop()}) killed.`),killedTunnels++}if(maybePrintJson(merged,{ok:!0,killed:killedAll,tunnels:killedTunnels}))return;blank(),success(`${icons.success} killed ${killedAgents} agent(s) and ${killedTunnels} tunnel(s).`);return}let profileName=opts.profile||opts.name||getVibecontrolsProfile();if(!merged.json)header(`Force killing profile: ${profileName}`),blank();let instance=await serviceManager.getStatus(profileName);if(instance){if(await serviceManager.kill(profileName),killedAll.push(profileName),!merged.json)success(`${icons.success} agent ${colors.bold(profileName)} pid=${instance.pid||"?"} killed.`)}else{let rtResult=await killAgentProfile(profileName);if(rtResult){if(killedAll.push(profileName),!merged.json)success(`${icons.success} ${rtResult}`)}else if(!merged.json)info(`${icons.info} no running agent service for profile ${profileName}.`)}let tunnelResult=await killProfileTunnel(profileName);if(tunnelResult){if(!merged.json)success(`${icons.success} ${tunnelResult}`)}else if(!merged.json)info(`${icons.info} no tunnel recorded for profile ${profileName}.`);maybePrintJson(merged,{ok:!0,killed:killedAll})}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(`Failed to kill: ${errMsg(err)}`)}})}function register5(program){program.command("list").alias("ls").description("List all VibeControls agent instances").option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(opts){try{let merged={...program.opts(),...opts},sm=new ServiceManager;await runMultimode({mode:pickOutputMode(merged),fetchData:()=>sm.listInstances(),plain:(instances)=>{if(header("Agent Instances"),blank(),!instances||instances.length===0){info(`${icons.info} No agent instances found.`),info(`Run ${colors.bold("vibe start")} to start an agent.`);return}let rows=instances.map((inst)=>({Name:inst.name,Status:formatStatus(inst.status),PID:inst.pid||"-",Port:inst.port||"-",Host:inst.config?.host||"-",Database:inst.config?.dbPath||"-",Started:inst.startTime?timeAgo(inst.startTime):"-"}));formatTable(rows),blank();let running=instances.filter((i)=>i.status==="running").length,stopped=instances.length-running;info(`${icons.info} ${instances.length} instance(s): ${colors.green(String(running))} running, ${colors.dim(String(stopped))} stopped`)},interactive:async(instances)=>{if(!instances||instances.length===0){header("Agent Instances"),blank(),info(`${icons.info} No agent instances found.`),info(`Run ${colors.bold("vibe start")} to start an agent.`);return}let rows=instances.map((inst)=>{let lines=[`${colors.bold(inst.name)} ${formatStatus(inst.status)}`,"",` PID: ${inst.pid??"-"}`,` Port: ${inst.port??"-"}`,` Host: ${inst.config?.host??"-"}`,` Database: ${inst.config?.dbPath??"-"}`,` Started: ${inst.startTime?timeAgo(inst.startTime):"-"}`];return{id:inst.name,label:inst.name,hint:inst.status==="running"?"running":"stopped",detail:lines.join(`
|
|
4
4
|
`)}});await interactiveTable({title:`vibe list \u2014 ${instances.length} instance(s)`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"})},json:(instances)=>instances.map((inst)=>({name:inst.name,status:inst.status,pid:inst.pid??null,port:inst.port??null,host:inst.config?.host??null,database:inst.config?.dbPath??null,startTime:inst.startTime??null}))})}catch(err){fail(`Failed to list instances: ${err.message}`)}})}var MAX_TAIL_LINES=5000;function register6(program){program.command("logs").description("View logs for a VibeControls agent instance").option("-n, --name <name>","Agent instance name","default").option("-f, --follow","Follow log output",!1).option("--tail <lines>","Number of lines to show from the end","100").option("--json","Emit JSON (one object per line, banners suppressed)").option("--plain","Force plain text output").action(async function(opts){try{let merged={...program.opts(),...opts},serviceManager=new ServiceManager,tail=parseInt(merged.tail??"100",10);if(isNaN(tail)||tail<1||tail>MAX_TAIL_LINES){fail(`Invalid tail value. Must be between 1 and ${MAX_TAIL_LINES}.`);return}let name=merged.name??"default";if(!await serviceManager.getStatus(name)){fail(`Agent instance ${colors.bold(name)} not found.`);return}if(!merged.json){if(merged.follow)header(`Following logs: ${name}`),info(`${icons.info} Press Ctrl+C to stop following.`);else header(`Logs: ${name}`);blank()}await serviceManager.showLogs(name,{follow:!!merged.follow,tail})}catch(err){fail(`Failed to get logs: ${errMsg(err)}`)}})}function register7(program){program.command("setup").description("Install system dependencies and (optionally) write gateway auth config").option("--check","Only check dependencies without installing",!1).option("--client-id <id>","OAuth app client ID (for backend authentication)").option("--client-secret <secret>","OAuth app client secret").option("--workspace-id <id>","Workspace UUID this agent belongs to").option("--global-gateway-url <url>","Global public gateway URL (default: http://localhost:4000/global/graphql)").option("--workspace-gateway-url <url>","Workspace gateway URL (default: http://localhost:4001/workspaces/graphql)").option("--storage-adapter <name>","Named storage adapter (default: skalex, or custom:<path>)").option("--json","Emit JSON").action(async function(options){let merged={...program.opts(),...options},configured=[];try{if(!merged.json)header("VibeControls Agent Setup");let update={};if(options.clientId)update.clientId=options.clientId;if(options.clientSecret)update.clientSecret=options.clientSecret;if(options.workspaceId)update.workspaceId=options.workspaceId;if(options.globalGatewayUrl)update.globalGatewayUrl=assertAllowedGatewayUrl(options.globalGatewayUrl,"globalGatewayUrl");if(options.workspaceGatewayUrl)update.workspaceGatewayUrl=assertAllowedGatewayUrl(options.workspaceGatewayUrl,"workspaceGatewayUrl");if(options.storageAdapter)update.storageAdapter=options.storageAdapter;if(Object.keys(update).length>0){let written=writeAgentConfig(update);if(configured.push(...Object.keys(update)),!merged.json){success(`Agent config written to ${getAgentConfigPath()}`),kv("clientId",written.clientId?colors.dim("(set)"):colors.dim("(unset)")),kv("clientSecret",written.clientSecret?colors.dim("(set)"):colors.dim("(unset)")),kv("workspaceId",written.workspaceId??colors.dim("(unset)"));let defaults=getAgentConfigDefaults();if(kv("globalGatewayUrl",written.globalGatewayUrl??defaults.globalGatewayUrl),kv("workspaceGatewayUrl",written.workspaceGatewayUrl??defaults.workspaceGatewayUrl),written.storageAdapter)kv("storageAdapter",written.storageAdapter);blank()}}else{let existing=readAgentConfig();if((existing.clientId||existing.clientSecret||existing.workspaceId)&&!merged.json)info(`Using existing agent config at ${getAgentConfigPath()} (pass --client-id/--client-secret/--workspace-id to update).`),blank()}if(!merged.json)info("Checking dependencies..."),blank();let deps=checkDependencies(),missing=[],installed=[];for(let dep of deps){if(dep.installed)installed.push(dep.name);if(!merged.json){let icon=dep.installed?icons.success:icons.error,version=dep.version?colors.dim(` (${dep.version})`):"",required=dep.required?"":colors.dim(" [optional]");kv(`${icon} ${dep.name}`,`${dep.installed?colors.green("installed"):colors.red("missing")}${version}${required}`)}if(!dep.installed)missing.push(dep.name)}if(!merged.json)blank();if(missing.length===0){if(maybePrintJson(merged,{ok:!0,installed,configured}))return;success("All dependencies are installed.");return}if(options.check){if(merged.json){maybePrintJson(merged,{ok:!1,installed,missing,configured});return}warn(`Missing: ${missing.join(", ")}`),info(`Run ${colors.bold("vibe setup")} to install them.`),process.exit(1)}if(!merged.json)info(`Installing missing dependencies: ${missing.join(", ")}...`),blank();let result=await installDependencies(missing),newlyInstalled=missing.filter((n)=>!result.failed.includes(n));if(result.failed.length>0){if(merged.json){maybePrintJson(merged,{ok:!1,installed:[...installed,...newlyInstalled],failed:result.failed,configured});return}warn(`${result.failed.length} tool(s) failed: ${result.failed.join(", ")}`),info("You may need to install manually or use sudo."),process.exit(1)}else{if(maybePrintJson(merged,{ok:!0,installed:[...installed,...newlyInstalled],configured}))return;success("All dependencies installed successfully.")}}catch(err){let message=err instanceof Error?errMsg(err):String(err);if(merged.json){maybePrintJson(merged,{ok:!1,error:message});return}fail(`Setup failed: ${message}`)}})}import{join as join4}from"path";async function getPackageMetadata(){let depths=["..","../..","../../.."];for(let depth of depths)try{let pkgPath=join4(import.meta.dir,depth,"package.json"),data=await Bun.file(pkgPath).json(),name=data.name,version=data.version;if(name&&version){let publishConfig=data.publishConfig;return{name,version,registry:publishConfig?.registry??getPluginRegistry()}}}catch{}throw Error("package.json not found")}async function fetchRemoteManifest(pkg,version,registry){try{let result=Bun.spawnSync([process.platform==="win32"?"npm.cmd":"npm","view",`${pkg}@${version}`,"--json",`--registry=${registry}`],{stdout:"pipe",stderr:"pipe"});if(result.exitCode!==0)return null;let parsed=JSON.parse(result.stdout.toString());return Array.isArray(parsed)?parsed[0]??null:parsed}catch{return null}}function compareSemver(a,b){let parse=(v)=>v.replace(/^[\^~>=<v]+/,"").split(/[.+-]/).map((p)=>Number(p)||0),aa=parse(a),bb=parse(b);for(let i=0;i<Math.max(aa.length,bb.length);i++){let ai=aa[i]??0,bi=bb[i]??0;if(ai!==bi)return ai-bi}return 0}function checkBunCompat(required,current){if(!required)return{ok:!0,message:""};let cleaned=required.replace(/^[\^~]/,"").replace(/^>=?\s*/,"");if(compareSemver(current,cleaned)>=0)return{ok:!0,message:`bun ${current} >= ${cleaned}`};return{ok:!1,message:`requires bun ${required}, this process is bun ${current}`}}function getInstalledPath(pkg){try{let npmCmd=process.platform==="win32"?"npm.cmd":"npm",r=Bun.spawnSync([npmCmd,"root","-g"],{stdout:"pipe",stderr:"pipe"});if(r.exitCode!==0)return null;return`${r.stdout.toString().trim()}/${pkg}`}catch{return null}}function smokeTestInstalled(pkg){let root=getInstalledPath(pkg);if(!root)return{ok:!0,step:"(skipped, install root unknown)"};let cli=`${root}/dist/cli.js`,run=(args,step,timeoutMs)=>{try{let runtime=process.platform==="win32"?"bun.exe":"bun",r=Bun.spawnSync([runtime,cli,...args],{stdout:"pipe",stderr:"pipe",timeout:timeoutMs});if(r.exitCode===0)return{ok:!0,step};return{ok:!1,step,detail:r.stderr.toString().slice(0,300)||`exit ${r.exitCode}`}}catch(err){return{ok:!1,step,detail:String(err)}}},v=run(["--version"],"vibe --version",1e4);if(!v.ok)return v;let i=run(["info"],"vibe info",15000);if(!i.ok)return i;let p=run(["plugin","list","--json"],"vibe plugin list",30000);if(!p.ok)return p;return{ok:!0,step:"all checks passed"}}async function maybeAutoRestart(name){try{let sm=new ServiceManager,status=await sm.getStatus(name);if(!status||status.status!=="running")return{kind:"not-running"};let agentUrl=`http://localhost:${status.port}`,apiKey;try{apiKey=await resolveApiKey(agentUrl)}catch{}let headers={};if(apiKey)headers["x-agent-api-key"]=apiKey;let POLL_MS=1000,MAX_MS=60000,started=Date.now();while(Date.now()-started<MAX_MS){try{let res=await fetch(`${agentUrl}/api/profiles/${encodeURIComponent(name)}/stats/inflight`,{headers,signal:AbortSignal.timeout(2000)});if(res.ok){let body=await res.json();if(typeof body.inflight==="number"&&body.inflight===0){let cfg=status.config;return await sm.restart(name,cfg),{kind:"restarted"}}}}catch{}await new Promise((r)=>setTimeout(r,POLL_MS))}return{kind:"still-busy"}}catch(err){return{kind:"error",message:err instanceof Error?errMsg(err):String(err)}}}function register8(program){program.command("update").description("Check for and install VibeControls agent updates").option("--check","Only check for updates without installing",!1).option("--allow-incompatible","Override engines.bun compatibility check (use with care)",!1).option("--no-restart","Skip the post-install idle-aware restart of any running agent").option("-n, --name <name>","Agent profile name to restart after update","default").option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts};try{if(!merged.json)header("VibeControls Agent Update"),blank();let metadata;try{metadata=await getPackageMetadata()}catch{fail("Could not read package metadata.");return}if(!merged.json)info(`${icons.info} Checking for updates...`),blank();let latestVersion;try{let result=Bun.spawnSync([process.platform==="win32"?"npm.cmd":"npm","view",metadata.name,"version",`--registry=${metadata.registry}`],{stdout:"pipe",stderr:"pipe"});if(result.exitCode!==0)throw Error("npm view failed");latestVersion=result.stdout.toString().trim()}catch{fail("Could not check for updates. Ensure you have internet connectivity and npm is available.");return}if(!merged.json)kv("Package",colors.bold(metadata.name)),kv("Registry",colors.bold(metadata.registry)),kv("Current version",colors.bold(metadata.version)),kv("Latest version",colors.bold(latestVersion)),blank();if(metadata.version===latestVersion){if(maybePrintJson(merged,{ok:!0,current:metadata.version,latest:latestVersion,upToDate:!0}))return;success(`${icons.success} You are running the latest version.`);return}if(opts.check){if(maybePrintJson(merged,{ok:!0,current:metadata.version,latest:latestVersion,upToDate:!1}))return;warn(`${icons.warning} A new version is available: ${colors.green(latestVersion)}`),info(`${icons.info} Run ${colors.bold("vibe update")} to install the update.`);return}let remoteManifest=await fetchRemoteManifest(metadata.name,latestVersion,metadata.registry);if(!remoteManifest)warn(`${icons.warning} Could not fetch remote manifest; skipping engine compat check.`);else{let bunVersion=typeof Bun<"u"?Bun.version:"0.0.0",compat=checkBunCompat(remoteManifest.engines?.bun,bunVersion);if(!compat.ok){if(!opts.allowIncompatible){fail(`${icons.error} Refusing to upgrade: ${compat.message}. Pass --allow-incompatible to override.`);return}warn(`${icons.warning} Engine mismatch (${compat.message}); proceeding due to --allow-incompatible.`)}else kv("Engine check",colors.bold(compat.message))}let installRoot=getInstalledPath(metadata.name),previousVersion=metadata.version;info(`${icons.info} Updating to ${colors.bold(latestVersion)}...`),blank();try{if(Bun.spawnSync([process.platform==="win32"?"npm.cmd":"npm","install","-g",`${metadata.name}@${latestVersion}`,`--registry=${metadata.registry}`],{stdout:"inherit",stderr:"inherit"}).exitCode!==0)throw Error("install failed")}catch{fail(`Failed to install update. Try running with sudo: ${colors.bold(`sudo npm install -g ${metadata.name}@${latestVersion} --registry=${metadata.registry}`)}`);return}let smoke=smokeTestInstalled(metadata.name);if(!smoke.ok){if(warn(`${icons.warning} Post-install smoke test failed at "${smoke.step}". Rolling back to ${previousVersion}\u2026`),smoke.detail)warn(`${icons.warning} Details: ${smoke.detail}`);try{if(Bun.spawnSync([process.platform==="win32"?"npm.cmd":"npm","install","-g",`${metadata.name}@${previousVersion}`,`--registry=${metadata.registry}`],{stdout:"inherit",stderr:"inherit"}).exitCode!==0)throw Error("rollback npm install failed");warn(`${icons.warning} Rolled back to ${previousVersion}. Investigate the new version before retrying.`)}catch{fail(`${icons.error} Rollback failed. Manually run: ${colors.bold(`npm install -g ${metadata.name}@${previousVersion}`)}`)}return}let shouldAutoRestart=opts.restart!==!1,restartOutcome={kind:"skipped"};if(shouldAutoRestart){if(!merged.json)blank(),info(`${icons.info} Waiting for agent ${colors.bold(opts.name)} to become idle before restart\u2026`);if(restartOutcome=await maybeAutoRestart(opts.name),!merged.json)switch(restartOutcome.kind){case"restarted":success(`${icons.success} Restarted agent ${colors.bold(opts.name)} on the new version.`);break;case"not-running":info(`${icons.info} No running agent for profile ${colors.bold(opts.name)}; nothing to restart.`);break;case"still-busy":warn(`${icons.warning} Agent still serving requests after 60s \u2014 run ${colors.bold("vibe restart")} when it's quiet.`);break;case"error":warn(`${icons.warning} Auto-restart failed: ${restartOutcome.message}. Run ${colors.bold("vibe restart")} manually.`);break}}if(maybePrintJson(merged,{ok:!0,current:latestVersion,previous:previousVersion,latest:latestVersion,restart:restartOutcome.kind,...restartOutcome.kind==="error"?{restartError:restartOutcome.message}:{}}))return;if(blank(),success(`${icons.success} Updated to ${colors.bold(latestVersion)} (smoke: ${smoke.step}).`),installRoot)info(`${icons.info} Installed at ${installRoot}.`);if(!shouldAutoRestart)info(`${icons.info} --no-restart set \u2014 restart running agents manually for the update to take effect.`)}catch(err){let message=err instanceof Error?errMsg(err):String(err);if(merged.json){maybePrintJson(merged,{ok:!1,error:message});return}fail(`Update failed: ${message}`)}}),program.command("rollback").description("Roll the global agent install back to a specific version").argument("<version>","Target CalVer version (e.g. 2026.428.3)").action(async(version)=>{try{header("VibeControls Agent Rollback");let metadata=await getPackageMetadata();if(kv("Package",colors.bold(metadata.name)),kv("Current version",colors.bold(metadata.version)),kv("Target version",colors.bold(version)),blank(),info(`${icons.info} Rolling back\u2026`),Bun.spawnSync([process.platform==="win32"?"npm.cmd":"npm","install","-g",`${metadata.name}@${version}`,`--registry=${metadata.registry}`],{stdout:"inherit",stderr:"inherit"}).exitCode!==0){fail("Rollback failed.");return}success(`${icons.success} Rolled back to ${colors.bold(version)}.`)}catch(err){fail(`Rollback failed: ${err instanceof Error?errMsg(err):String(err)}`)}})}import{existsSync as existsSync4,mkdirSync,writeFileSync as writeFileSync2,unlinkSync as unlinkSync3,readFileSync as readFileSync3}from"fs";import{join as join5}from"path";import{homedir}from"os";var SAFE_COMPLETION_NAME_RE=/^[A-Za-z0-9][A-Za-z0-9:_-]{0,63}$/,SAFE_COMPLETION_OPTION_RE=/^--[A-Za-z0-9][A-Za-z0-9-]{0,63}$/;function safeCompletionName(name){return SAFE_COMPLETION_NAME_RE.test(name)?name:null}function safeCompletionWords(words){return words.filter((word)=>safeCompletionName(word))}function safeCompletionOptions(options){return options.filter((option)=>SAFE_COMPLETION_OPTION_RE.test(option))}function collectCommands(cmd){return cmd.commands.flatMap((sub)=>{let name=safeCompletionName(sub.name());if(!name)return[];return[{name,description:sub.description()||"",options:sub.options.map((opt)=>({flags:opt.flags,description:opt.description||""})),subcommands:collectCommands(sub)}]})}function topLevelNames(cmds){return safeCompletionWords(cmds.map((c)=>c.name))}function longOptions(cmds,name){let cmd=cmds.find((c)=>c.name===name);if(!cmd)return[];let opts=[];for(let o of cmd.options){let match=o.flags.match(/--([\w-]+)/);if(match)opts.push(`--${match[1]}`)}return safeCompletionOptions(opts)}function generateBash(cmds){let top=topLevelNames(cmds),caseArms=cmds.filter((c)=>c.subcommands.length>0).map((c)=>({name:c.name,subs:safeCompletionWords(c.subcommands.map((s)=>s.name))})).map((g)=>` ${g.name})
|
|
5
5
|
COMPREPLY=($(compgen -W "${g.subs.join(" ")}" -- "\${cur}"))
|
|
6
6
|
return 0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{denyNonLocalMutation,isLikelyLocalRequest}from"./index-49a60qjr.js";import{autoReportToBackend,stopProfileReconciler,stopTunnelSync}from"./index-
|
|
2
|
+
import{denyNonLocalMutation,isLikelyLocalRequest}from"./index-49a60qjr.js";import{autoReportToBackend,stopProfileReconciler,stopTunnelSync}from"./index-g9h64dn9.js";import{runAliasCaptured}from"./index-9e1fgxea.js";import{checkDependencies,installDependencies}from"./index-fg47j98r.js";import{ServiceManager}from"./index-34syaqa8.js";import{redactUnknownSecrets}from"./index-y5q0m3cx.js";import{getActiveContributorRegistry}from"./index-n3y918j2.js";import"./index-h6a1kkfk.js";import"./index-srbb2214.js";import{validateEvent}from"./index-3jez1q8j.js";import"./index-brtw3j8x.js";import{getAgentApiKey}from"./index-b4wy3jrt.js";import"./index-d3mz9vws.js";import{Elysia,t}from"./index-rnk0kny8.js";import{register}from"./index-njg2sn7v.js";import"./index-qbvtba0e.js";import{apiGet,apiPost,getAgentUrl,maybePrintJson,pickOutputMode,resolveProfileName,runMultimode}from"./index-wvabz3xh.js";import{blank,colors,errMsg,fail,formatBytes,formatDuration,formatStatus,formatTable,header,icons,info,kv,success,timeAgo}from"./index-r0qezdp7.js";import{interactiveDetail}from"./index-campp0wv.js";import{writeAgentConfig}from"./index-2pqv0bya.js";import{currentProfileOrNull,gatewayClient,getDaemonProfile}from"./index-7pdmqbj8.js";import{getVibecontrolsDir,getVibecontrolsProfile,getVibecontrolsProfilesDir,isPathInside}from"./index-bysm7taq.js";import"./index-zs58d1hc.js";import{__require}from"./index-e9rt4m94.js";function createLifecycleHooks(spec){let{name,onInit,onShutdown,onNuke,telemetryEventName,skipPlatforms}=spec;return{onServerStart:async(_app,hostServices)=>{if(skipPlatforms&&skipPlatforms.includes(process.platform)){process.stderr.write(`[${name}] skipping init on unsupported platform '${process.platform}'
|
|
3
3
|
`);return}if(onInit)await onInit(hostServices);if(telemetryEventName)hostServices.telemetry?.emit(telemetryEventName,{plugin:name})},onServerStop:async(hostServices)=>{if(onShutdown)await onShutdown(hostServices)},onNuke:async(hostServices,ctx)=>{if(onNuke)return onNuke(hostServices,ctx)}}}var BoundLogger=class{constructor(logger,source){this.logger=logger,this.source=source}logger;source;info(message,meta){this.logger?.info?.(this.source,message,meta)}warn(message,meta){this.logger?.warn?.(this.source,message,meta)}error(message,meta){this.logger?.error?.(this.source,message,meta)}debug(message,meta){this.logger?.debug?.(this.source,message,meta)}};import{existsSync,rmSync,statSync}from"fs";import{resolve}from"path";function wipeAgentDir(agentDir=getVibecontrolsDir()){let removed=[],safeAgentDir=resolve(agentDir),agentsDir=getVibecontrolsProfilesDir();if(!isPathInside(agentsDir,safeAgentDir))throw Error("Refusing to reset a path outside the agents directory");if(existsSync(safeAgentDir)&&statSync(safeAgentDir).isDirectory())rmSync(safeAgentDir,{recursive:!0,force:!0}),removed.push(safeAgentDir);return{removed,agentDir:safeAgentDir}}var ALIAS_PLUGIN_NAMESPACE="alias",ALIAS_NAME_RE=/^[a-zA-Z0-9_-]{1,64}$/,ALIAS_COMMAND_MAX=16384,ALIAS_DESCRIPTION_MAX=256,ALIAS_CWD_MAX=1024,RESERVED_ALIAS_NAMES=new Set(["help","start","stop","restart","kill","list","status","setup","update","completion","autostart","export","import","diagnostics","nuke","doctor","log-level","gc","deploy","network","profile","peer-bootstrap","gateway-auth","info","key","system","url","health","notify","tunnel","session","git","config","log","plugin","ai","audit","alias","run"]);class AliasValidationError extends Error{code;constructor(code,message){super(message);this.code=code,this.name="AliasValidationError"}}class AliasConflictError extends Error{constructor(name){super(`alias '${name}' already exists`);this.name="AliasConflictError"}}class AliasNotFoundError extends Error{constructor(name){super(`alias '${name}' not found`);this.name="AliasNotFoundError"}}function validateAliasName(name){if(typeof name!=="string"||!ALIAS_NAME_RE.test(name))throw new AliasValidationError("invalid_name",`alias name must match ${ALIAS_NAME_RE} (got '${name}')`);if(RESERVED_ALIAS_NAMES.has(name))throw new AliasValidationError("reserved_name",`alias name '${name}' is reserved`)}function validateAliasCommand(command){if(typeof command!=="string"||command.length===0)throw new AliasValidationError("invalid_command","alias command must be a non-empty string");if(command.length>ALIAS_COMMAND_MAX)throw new AliasValidationError("command_too_long",`alias command exceeds ${ALIAS_COMMAND_MAX} bytes`)}function validateAliasDescription(description){if(description===void 0)return;if(typeof description!=="string")throw new AliasValidationError("invalid_description","alias description must be a string");if(description.length>ALIAS_DESCRIPTION_MAX)throw new AliasValidationError("description_too_long",`alias description exceeds ${ALIAS_DESCRIPTION_MAX} chars`)}function validateAliasCwd(cwd){if(cwd===void 0||cwd===null||cwd==="")return;if(typeof cwd!=="string")throw new AliasValidationError("invalid_cwd","alias cwd must be a string");if(cwd.length>ALIAS_CWD_MAX)throw new AliasValidationError("cwd_too_long",`alias cwd exceeds ${ALIAS_CWD_MAX} chars`);if(cwd.includes("\x00"))throw new AliasValidationError("invalid_cwd","alias cwd must not contain NUL bytes")}function safeParse(value){if(!value)return;try{return JSON.parse(value)}catch{return}}async function listAliases(db){let rows=await db.getAllPluginState(ALIAS_PLUGIN_NAMESPACE),out=[];for(let row of rows){let parsed=safeParse(row.value);if(parsed)out.push(parsed)}return out.sort((a,b)=>a.name.localeCompare(b.name)),out}async function getAliasByName(db,name){return(await listAliases(db)).find((a)=>a.name===name)}function normalizeCwd(cwd){if(cwd===void 0||cwd===null)return null;let trimmed=cwd.trim();return trimmed===""?null:trimmed}async function createAlias(db,input){if(validateAliasName(input.name),validateAliasCommand(input.command),validateAliasDescription(input.description),validateAliasCwd(input.cwd),await getAliasByName(db,input.name))throw new AliasConflictError(input.name);let now=new Date().toISOString(),row={id:crypto.randomUUID(),name:input.name,command:input.command,description:input.description,cwd:normalizeCwd(input.cwd),profile:getVibecontrolsProfile(),createdAt:now,updatedAt:now};return await db.setPluginState(ALIAS_PLUGIN_NAMESPACE,row.id,JSON.stringify(row)),row}async function updateAlias(db,name,patch){let current=await getAliasByName(db,name);if(!current)throw new AliasNotFoundError(name);if(patch.command!==void 0)validateAliasCommand(patch.command);if(patch.description!==void 0)validateAliasDescription(patch.description);if(patch.cwd!==void 0)validateAliasCwd(patch.cwd);let next={...current,command:patch.command??current.command,description:patch.description!==void 0?patch.description:current.description,cwd:patch.cwd!==void 0?normalizeCwd(patch.cwd):current.cwd,updatedAt:new Date().toISOString()};return await db.setPluginState(ALIAS_PLUGIN_NAMESPACE,next.id,JSON.stringify(next)),next}async function deleteAlias(db,name){let current=await getAliasByName(db,name);if(!current)throw new AliasNotFoundError(name);await db.deletePluginState(ALIAS_PLUGIN_NAMESPACE,current.id)}function activeProfileConfigOpts(){let ctx=currentProfileOrNull()??getDaemonProfile();return{dir:ctx.dataDir,scope:ctx.name}}function createRoutes(deps){let{serviceRegistry,db}=deps;return new Elysia().get("/api-key",()=>({apiKey:getAgentApiKey()})).get("/audit",({query})=>{let q=query,limit=q.limit?Math.min(5000,Math.max(1,Number.parseInt(q.limit,10)||100)):100,events=getDaemonProfile().audit.listEvents({since:q.since,limit,event:q.event});return{events,count:events.length}},{query:t.Object({since:t.Optional(t.String()),limit:t.Optional(t.String()),event:t.Optional(t.String())})}).get("/setup/check",()=>{let deps2=checkDependencies();return{ready:deps2.filter((d)=>d.required).every((d)=>d.installed),dependencies:deps2}}).post("/setup/install",async({request,server,set})=>{let denied=denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_SETUP_INSTALL",server);if(denied)return set.status=403,{error:"Forbidden",message:denied};return await installDependencies()}).get("/url",async()=>{let tunnelProvider=serviceRegistry.getProvider("tunnel"),tunnelUrl=tunnelProvider&&tunnelProvider.getActiveTunnelUrl?await tunnelProvider.getActiveTunnelUrl():null,localUrl=`http://localhost:${process.env.PORT||3005}`;return{url:tunnelUrl||localUrl,tunnelUrl,localUrl,source:tunnelUrl?"tunnel":"local"}}).get("/lifecycle/state",()=>{return{state:serviceRegistry.getService("agent","lifecycle")?.getState()??"running"}}).post("/lifecycle/stop",async({set})=>{let lifecycle=serviceRegistry.getService("agent","lifecycle");if(!lifecycle)return set.status=500,{error:"Lifecycle manager not initialized"};return await lifecycle.stop(),{state:"stopped"}}).post("/lifecycle/start",async({set})=>{let lifecycle=serviceRegistry.getService("agent","lifecycle");if(!lifecycle)return set.status=500,{error:"Lifecycle manager not initialized"};return await lifecycle.start(),{state:"running"}}).post("/lifecycle/kill",async({set})=>{let lifecycle=serviceRegistry.getService("agent","lifecycle");if(!lifecycle)return set.status=500,{error:"Lifecycle manager not initialized"};return setTimeout(()=>lifecycle.kill(),100),{state:"killing"}}).post("/log-level",async({body,request,server,set})=>{let denied=denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_LOG_LEVEL",server);if(denied)return set.status=403,{error:"Forbidden",message:denied};let next=String(body.level??"").toLowerCase();if(!["debug","info","warn","error"].includes(next))return set.status=400,{error:"Invalid level",message:"level must be debug | info | warn | error"};return getDaemonProfile().logger.setLevel(next),getDaemonProfile().logger.info("agent",`Log level set to ${next} via API`),{ok:!0,level:next}},{body:t.Object({level:t.String()})}).post("/reset",async({body,request,server,set})=>{if(!body.confirm)return set.status=400,{error:"Reset confirmation required",message:"Send { confirm: true } to reset local agent state."};let denied=denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_RESET",server);if(denied)return set.status=403,{error:"Forbidden",message:denied};let lifecycle=serviceRegistry.getService("agent","lifecycle");try{if(lifecycle)await lifecycle.prepareForReset()}catch(err){getDaemonProfile().logger.warn("agent","prepareForReset failed during reset \u2014 proceeding to wipe anyway",{error:String(err)})}try{gatewayClient.configure({globalGatewayUrl:"",workspaceGatewayUrl:void 0,clientId:"",clientSecret:"",workspaceId:void 0})}catch{}await db.close();let{removed,agentDir}=wipeAgentDir();getDaemonProfile().logger.info("agent","Reset wiped local agent state",{agentDir,removed});let bytes=new Uint8Array(32);crypto.getRandomValues(bytes);let newApiKey=`vcak_${Buffer.from(bytes).toString("base64url")}`;return writeAgentConfig({"static-api-key":newApiKey},activeProfileConfigOpts()),getDaemonProfile().setBootState("awaiting-config"),set.status=200,{success:!0,state:"awaiting-config",...isLikelyLocalRequest(request,server)||process.env.VIBECONTROLS_RETURN_RESET_API_KEY==="1"?{newApiKey}:{},agentDir,removed}},{body:t.Object({confirm:t.Boolean()})}).post("/sessions/:sessionId/share",async({params,body,server,request,set})=>{if(!isLikelyLocalRequest(request,server)&&process.env.VIBECONTROLS_ALLOW_REMOTE_SESSION_SHARE!=="1")return set.status=403,{error:"Forbidden",message:"Session-share mint must come from a local request unless VIBECONTROLS_ALLOW_REMOTE_SESSION_SHARE=1."};let ttlMs=parseTtl(body?.expiresIn)??1800000,id=crypto.randomUUID(),bytes=new Uint8Array(32);crypto.getRandomValues(bytes);let key=`vcak_${Buffer.from(bytes).toString("base64url")}`,expiresAt=new Date(Date.now()+ttlMs).toISOString(),apiKeys=[...(await import("./agent-config-y63ddxgy.js")).readAgentConfig({dir:activeProfileConfigOpts().dir}).apiKeys??[],{id,name:`session-share:${params.sessionId}:${body?.label??"viewer"}`,key,scopes:["read"],expiresAt,createdAt:new Date().toISOString()}];(await import("./agent-config-y63ddxgy.js")).writeAgentConfig({apiKeys},activeProfileConfigOpts());let tunnelProvider=serviceRegistry.getProvider("tunnel"),joinUrl=`${((tunnelProvider&&tunnelProvider.getActiveTunnelUrl?await tunnelProvider.getActiveTunnelUrl():null)??`http://localhost:${process.env.PORT||3005}`).replace(/\/$/,"")}/ws/terminal?sessionId=${encodeURIComponent(params.sessionId)}&apiKey=${encodeURIComponent(key)}`;return getDaemonProfile().logger.info("session-share","Minted share credential",{sessionId:params.sessionId,keyId:id,expiresAt}),{ok:!0,keyId:id,apiKey:key,joinUrl,sessionId:params.sessionId,expiresAt}},{params:t.Object({sessionId:t.String()}),body:t.Optional(t.Object({permissions:t.Optional(t.Array(t.String())),expiresIn:t.Optional(t.String()),label:t.Optional(t.String())}))}).get("/aliases",async()=>{return{aliases:await listAliases(db)}}).get("/aliases/:name",async({params,set})=>{let alias=await getAliasByName(db,params.name);if(!alias)return set.status=404,{error:"Alias not found"};return alias},{params:t.Object({name:t.String()})}).post("/aliases",async({body,set})=>{try{let alias=await createAlias(db,body);return set.status=201,alias}catch(err){if(err instanceof AliasConflictError)return set.status=409,{error:"Alias already exists",message:err.message};if(err instanceof AliasValidationError)return set.status=400,{error:err.code,message:err.message};throw err}},{body:t.Object({name:t.String(),command:t.String(),description:t.Optional(t.String()),cwd:t.Optional(t.Union([t.String(),t.Null()]))})}).put("/aliases/:name",async({params,body,set})=>{try{return await updateAlias(db,params.name,body)}catch(err){if(err instanceof AliasNotFoundError)return set.status=404,{error:"Alias not found"};if(err instanceof AliasValidationError)return set.status=400,{error:err.code,message:err.message};throw err}},{params:t.Object({name:t.String()}),body:t.Object({command:t.Optional(t.String()),description:t.Optional(t.String()),cwd:t.Optional(t.Union([t.String(),t.Null()]))})}).delete("/aliases/:name",async({params,set})=>{try{return await deleteAlias(db,params.name),set.status=204,null}catch(err){if(err instanceof AliasNotFoundError)return set.status=404,{error:"Alias not found"};throw err}},{params:t.Object({name:t.String()})}).post("/aliases/:name/execute",async({params,body,set})=>{let alias=await getAliasByName(db,params.name);if(!alias)return set.status=404,{error:"Alias not found"};let args=body?.args??[],result=await runAliasCaptured(alias,args);getDaemonProfile().audit.emit("agent","alias.executed",{exit_code:result.exit_code});let telemetry=validateEvent("alias.executed",{exit_code:result.exit_code});if(!telemetry.ok)getDaemonProfile().logger.debug("alias",`telemetry validate failed: ${telemetry.reason}`);if(result.timed_out)set.status=408;return{exit_code:result.exit_code,stdout:result.stdout,stderr:result.stderr,duration_ms:result.duration_ms,truncated:result.truncated,cwd:result.cwd}},{params:t.Object({name:t.String()}),body:t.Optional(t.Object({args:t.Optional(t.Array(t.String()))}))}).post("/sessions/share/:keyId/revoke",async({params,server,request,set})=>{if(!isLikelyLocalRequest(request,server)&&process.env.VIBECONTROLS_ALLOW_REMOTE_SESSION_SHARE!=="1")return set.status=403,{error:"Forbidden"};let cfgMod=await import("./agent-config-y63ddxgy.js"),cfgOpts=activeProfileConfigOpts(),cfg=cfgMod.readAgentConfig({dir:cfgOpts.dir}),before=(cfg.apiKeys??[]).length,apiKeys=(cfg.apiKeys??[]).filter((k)=>k.id!==params.keyId);if(apiKeys.length===before)return set.status=404,{error:"No such share key"};return cfgMod.writeAgentConfig({apiKeys},cfgOpts),{ok:!0,revoked:params.keyId}},{params:t.Object({keyId:t.String()})})}function parseTtl(input){if(!input)return null;let m=/^(\d+)([smhdw]?)$/.exec(input.trim());if(!m)return null;let n=Number(m[1]),unit=m[2]||"m";return unit==="s"?n*1000:unit==="m"?n*60000:unit==="h"?n*3600000:unit==="d"?n*86400000:n*7*86400000}var DEFAULT_AGENT_URL="http://localhost:3005";function register2(program){program.command("health").description("Check health of a VibeControls agent instance").option("-n, --name <name>","Agent instance name").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(opts){try{let merged={...program.opts(),...opts};await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{if(merged.name){let result=await new ServiceManager().checkHealth(merged.name);return{mode:"named",agentUrl:getAgentUrl(merged),name:merged.name,healthy:result.healthy,details:result.details??{}}}let agentUrl=getAgentUrl(merged);try{let data=await apiGet(agentUrl,"/health");return{mode:"url",agentUrl,healthy:!0,details:data}}catch(err){return{mode:"url",agentUrl,healthy:!1,details:{status:"unhealthy",...err instanceof Error?{error:errMsg(err)}:{}}}}},plain:(data)=>{if(header("Agent Health"),blank(),data.mode==="named"){if(!data.healthy){fail(`Agent instance ${colors.bold(data.name??"")} is not responding.`);return}let details=data.details;if(kv("Instance",data.name??""),kv("Status",formatStatus(details.status??"healthy")),details.uptime!==void 0)kv("Uptime",`${details.uptime}s`);if(details.checks){blank(),info(`${icons.info} Health checks:`);for(let[name,check]of Object.entries(details.checks)){let icon=check.status==="healthy"?icons.success:icons.error;kv(` ${icon} ${name}`,formatStatus(check.status))}}}else{if(kv("URL",data.agentUrl),kv("Status",formatStatus(data.details.status??"unhealthy")),data.details.version)kv("Version",data.details.version);if(data.details.uptime!==void 0)kv("Uptime",`${data.details.uptime}s`);if(data.details.timestamp)kv("Timestamp",data.details.timestamp);if(data.details.checks){blank(),info(`${icons.info} Health checks:`);for(let[name,check]of Object.entries(data.details.checks)){let icon=check.status==="healthy"?icons.success:icons.error;kv(` ${icon} ${name}`,formatStatus(check.status))}}}if(blank(),data.healthy)success(`${icons.success} Agent is healthy.`)},interactive:async(data)=>{let lines=[];if(data.mode==="named")lines.push(`Instance: ${data.name??""}`);else lines.push(`URL: ${data.agentUrl}`);if(lines.push(`Status: ${formatStatus(data.details.status??(data.healthy?"healthy":"unhealthy"))}`),data.details.version)lines.push(`Version: ${data.details.version}`);if(data.details.uptime!==void 0)lines.push(`Uptime: ${data.details.uptime}s`);if(data.details.timestamp)lines.push(`Time: ${data.details.timestamp}`);if(data.details.checks){lines.push(""),lines.push("Checks:");for(let[name,check]of Object.entries(data.details.checks))lines.push(` ${name}: ${check.status}`)}await interactiveDetail({title:"Agent Health",body:lines.join(`
|
|
4
4
|
`),footer:"press q to exit"})},json:(data)=>({mode:data.mode,agentUrl:data.agentUrl,name:data.name??null,healthy:data.healthy,details:data.details})})}catch(err){fail(`Health check failed: ${errMsg(err)}`)}})}import{platform,arch,release,hostname,cpus,totalmem}from"os";var DEFAULT_AGENT_URL2="http://localhost:3005";function register3(program){program.command("info").description("Show local and remote VibeControls agent information").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL2).option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(opts){try{let merged={...program.opts(),...opts},agentUrl=getAgentUrl(merged),profile=encodeURIComponent(resolveProfileName(merged));await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{let local={hostname:hostname(),platform:platform(),arch:arch(),release:release(),cpus:cpus().length,totalMemory:totalmem(),nodeVersion:process.version,bunVersion:process.versions.bun};try{let remote=await apiGet(agentUrl,`/api/profiles/${profile}/agent/version`);return{local,agentUrl,remote}}catch(err){return{local,agentUrl,remote:null,remoteError:err instanceof Error?errMsg(err):String(err)}}},plain:(data)=>{if(header("VibeControls Agent Info"),blank(),info(`${icons.info} Local System`),kv("Hostname",data.local.hostname),kv("Platform",`${data.local.platform} ${data.local.arch}`),kv("OS Release",data.local.release),kv("CPUs",String(data.local.cpus)),kv("Total Memory",formatBytes(data.local.totalMemory)),kv("Node.js",data.local.nodeVersion),data.local.bunVersion)kv("Bun",data.local.bunVersion);if(blank(),info(`${icons.info} Remote Agent`),kv("URL",data.agentUrl),data.remote){if(kv("Version",colors.bold(data.remote.version)),data.remote.build)kv("Build",data.remote.build);if(data.remote.commit)kv("Commit",data.remote.commit);if(data.remote.nodeVersion)kv("Node.js (remote)",data.remote.nodeVersion);if(data.remote.bunVersion)kv("Bun (remote)",data.remote.bunVersion)}else kv("Status",colors.dim("not reachable"));blank()},interactive:async(data)=>{let lines=[];if(lines.push("Local System"),lines.push(` Hostname: ${data.local.hostname}`),lines.push(` Platform: ${data.local.platform} ${data.local.arch}`),lines.push(` OS Release: ${data.local.release}`),lines.push(` CPUs: ${data.local.cpus}`),lines.push(` Total Memory: ${formatBytes(data.local.totalMemory)}`),lines.push(` Node.js: ${data.local.nodeVersion}`),data.local.bunVersion)lines.push(` Bun: ${data.local.bunVersion}`);if(lines.push(""),lines.push("Remote Agent"),lines.push(` URL: ${data.agentUrl}`),data.remote){if(lines.push(` Version: ${data.remote.version}`),data.remote.build)lines.push(` Build: ${data.remote.build}`);if(data.remote.commit)lines.push(` Commit: ${data.remote.commit}`);if(data.remote.nodeVersion)lines.push(` Node.js: ${data.remote.nodeVersion} (remote)`);if(data.remote.bunVersion)lines.push(` Bun: ${data.remote.bunVersion} (remote)`)}else lines.push(" Status: not reachable");await interactiveDetail({title:"VibeControls Agent Info",body:lines.join(`
|
|
5
5
|
`),footer:"press q to exit"})},json:(data)=>({local:data.local,agentUrl:data.agentUrl,remote:data.remote,remoteError:data.remoteError??null})})}catch(err){fail(`Failed to get info: ${errMsg(err)}`)}})}var DEFAULT_AGENT_URL3="http://localhost:3005";function register4(program){program.command("system").description("Show system information for a running VibeControls agent").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL3).option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(opts){try{let merged={...program.opts(),...opts},agentUrl=getAgentUrl(merged),profile=encodeURIComponent(resolveProfileName(merged));await runMultimode({mode:pickOutputMode(merged),fetchData:()=>apiGet(agentUrl,`/api/profiles/${profile}/agent/system`),plain:(data)=>{if(header("Agent System Information"),blank(),info(`${icons.info} Agent`),kv("Version",colors.bold(data.agentVersion)),data.bunVersion)kv("Bun",data.bunVersion);if(data.environment)kv("Environment",data.environment);kv("Uptime",formatDuration(data.uptime)),blank(),info(`${icons.info} Platform`),kv("OS",`${data.platform} ${data.arch}`),kv("Hostname",data.hostname),kv("Release",data.release),blank(),info(`${icons.info} Resources`),kv("CPUs",String(data.cpus)),kv("Total Memory",formatBytes(data.totalMemory)),kv("Free Memory",formatBytes(data.freeMemory)),blank(),info(`${icons.info} Paths`),kv("Home",data.homeDir),kv("CWD",data.cwd),blank()},interactive:async(data)=>{let lines=["Agent",` Version: ${data.agentVersion}`,...data.bunVersion?[` Bun: ${data.bunVersion}`]:[],...data.environment?[` Environment: ${data.environment}`]:[],` Uptime: ${formatDuration(data.uptime)}`,"","Platform",` OS: ${data.platform} ${data.arch}`,` Hostname: ${data.hostname}`,` Release: ${data.release}`,"","Resources",` CPUs: ${data.cpus}`,` Total Mem: ${formatBytes(data.totalMemory)}`,` Free Mem: ${formatBytes(data.freeMemory)}`,"","Paths",` Home: ${data.homeDir}`,` CWD: ${data.cwd}`];await interactiveDetail({title:"Agent System Information",body:lines.join(`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{CliContributorRegistry,createIframeBridge}from"./index-
|
|
2
|
+
import{CliContributorRegistry,createIframeBridge}from"./index-n3y918j2.js";import{PluginManager,ServiceRegistry}from"./index-h6a1kkfk.js";import{getOsAdapter}from"./index-srbb2214.js";import{mountProfileRoutes}from"./index-brtw3j8x.js";import{getAgentApiKey}from"./index-b4wy3jrt.js";import{readAgentConfigWithSecrets,writeAgentConfig}from"./index-2pqv0bya.js";import{assertAllowedGatewayUrl,gatewayClient,getDaemonProfile,getOrCreateProfile,profileRegistry,sanitizeOperatorTunnelUrl,sanitizeTunnelUrl}from"./index-7pdmqbj8.js";import{getVibecontrolsProductDir,validateAgentName}from"./index-bysm7taq.js";import{__require}from"./index-e9rt4m94.js";var RETRY_BACKOFFS_MS=[0,5000,15000,30000,60000],RATE_LIMIT_BUFFER_MS=2000,RATE_LIMIT_MIN_WAIT_MS=5000,RATE_LIMIT_MAX_WAIT_MS=120000,RATE_LIMIT_MAX_RETRIES=10,MAX_GATEWAY_RESPONSE_BYTES=1048576;class RateLimitError extends Error{resetAt;resource;constructor(message,resource,resetAt){super(message);this.name="RateLimitError",this.resource=resource,this.resetAt=resetAt}}function detectRateLimit(errors,resource){if(!errors?.length)return null;let hit=errors.find((e)=>e.extensions?.code==="RATE_LIMITED");if(!hit)return null;let resetRaw=hit.extensions?.resetAt,resetAt=null;if(resetRaw){let parsed=new Date(resetRaw);if(!Number.isNaN(parsed.getTime()))resetAt=parsed}return new RateLimitError(`${resource} rate limited${resetAt?` until ${resetAt.toISOString()}`:""}`,resource,resetAt)}function computeRateLimitDelay(err){if(!err.resetAt)return 60000;let waitMs=err.resetAt.getTime()-Date.now()+RATE_LIMIT_BUFFER_MS;return Math.min(Math.max(waitMs,RATE_LIMIT_MIN_WAIT_MS),RATE_LIMIT_MAX_WAIT_MS)}async function readResponseTextCapped(response,maxBytes){let contentLength=response.headers.get("content-length");if(contentLength){let parsed=Number.parseInt(contentLength,10);if(Number.isInteger(parsed)&&parsed>maxBytes)throw Error(`Gateway response is too large (${parsed} bytes)`)}if(!response.body)return"";let reader=response.body.getReader(),chunks=[],total=0;try{while(!0){let{done,value}=await reader.read();if(done)break;if(!value)continue;if(total+=value.byteLength,total>maxBytes)throw await reader.cancel().catch(()=>{return}),Error(`Gateway response exceeds ${maxBytes} bytes`);chunks.push(value)}}finally{reader.releaseLock()}let merged=new Uint8Array(total),offset=0;for(let chunk of chunks)merged.set(chunk,offset),offset+=chunk.byteLength;return new TextDecoder().decode(merged)}async function readGraphqlJson(response,resource){let raw=await readResponseTextCapped(response,MAX_GATEWAY_RESPONSE_BYTES),data;try{data=raw?JSON.parse(raw):{}}catch(err){let preview=raw.slice(0,240).replace(/\s+/g," ");throw Error(`${resource} returned non-JSON response (${response.status} ${response.statusText})${preview?`: ${preview}`:""}`,{cause:err})}if(!response.ok){let preview=raw.slice(0,240).replace(/\s+/g," ");throw Error(`${resource} failed (${response.status} ${response.statusText})${preview?`: ${preview}`:""}`)}return data}function ensureGraphqlSuffix(url){return url.endsWith("/graphql")?url:`${url}/graphql`}function isLocalDevGatewayHost(url){let host;try{host=new URL(url).hostname.toLowerCase().replace(/^\[|\]$/g,"")}catch{return!1}if(host==="localhost"||host==="127.0.0.1"||host==="::1"||host==="0.0.0.0"||host==="host.docker.internal")return!0;return host.endsWith(".localhost")||host.endsWith(".local")||host.endsWith(".local.burdenoff.com")}function devTlsFetchInit(url){if(!(process.env.VIBE_DEV_INSECURE_TLS==="1"||process.env.NODE_TLS_REJECT_UNAUTHORIZED==="0")&&!isLocalDevGatewayHost(url))return{};return{tls:{rejectUnauthorized:!1}}}async function authenticateApp(globalGatewayUrl,clientId,clientSecret){let res=await fetch(ensureGraphqlSuffix(globalGatewayUrl),{method:"POST",...devTlsFetchInit(globalGatewayUrl),headers:{"Content-Type":"application/json"},body:JSON.stringify({query:`mutation AuthenticateApp($input: AuthenticateAppInput!) {
|
|
3
3
|
authenticateApp(input: $input) { accessToken expiresIn }
|
|
4
4
|
}`,variables:{input:{clientId,clientSecret,scopes:[]}}}),signal:AbortSignal.timeout(15000)}),data=await readGraphqlJson(res,"authenticateApp"),rl=detectRateLimit(data.errors,"authenticateApp");if(rl)throw rl;if(data.errors?.length){let codes=data.errors.map((e)=>e.extensions?.code??"UNKNOWN").join(",");throw Error(`authenticateApp errors [${codes}]: ${data.errors.map((e)=>e.message).join("; ")}`)}let token=data.data?.authenticateApp?.accessToken;if(!token)throw Error("authenticateApp returned no token");return token}async function issueWorkspaceToken(workspaceGatewayUrl,appToken,workspaceId,organizationId){let res=await fetch(ensureGraphqlSuffix(workspaceGatewayUrl),{method:"POST",...devTlsFetchInit(workspaceGatewayUrl),headers:{"Content-Type":"application/json",Authorization:`Bearer ${appToken}`},body:JSON.stringify({query:`mutation IssueAgentWorkspaceToken($input: IssueWorkspaceTokenInput!) {
|
|
5
5
|
issueWorkspaceToken(input: $input) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import{getDaemonProfile,profileRegistry,registerAllowedTunnelSuffix}from"./index-7pdmqbj8.js";import{getPluginRegistry,getVibecontrolsDir}from"./index-bysm7taq.js";import{__require}from"./index-e9rt4m94.js";import{existsSync,mkdirSync,readFileSync,renameSync,realpathSync,statSync,writeFileSync}from"fs";import{homedir as osHomedir}from"os";import{dirname,isAbsolute,join,relative}from"path";import{fileURLToPath as fileURLToPath2}from"url";var PROVIDER_TYPES=["tunnel","session","ai","plan"];class ServiceRegistry{providers=new Map;providerOrder=new Map;defaults=new Map;services=new Map;db;constructor(db){this.db=db}getDb(){return this.db}setProviderDefault(type,pluginName){this.defaults.set(type,pluginName)}clearProviderDefault(type,pluginName){if(!pluginName||this.defaults.get(type)===pluginName)this.defaults.delete(type)}async hydrateDefaultsFromDb(){if(!this.db)return;for(let type of PROVIDER_TYPES)try{let name=await this.db.getConfig(`provider:default:${type}`);if(name)this.defaults.set(type,name)}catch{}}registerProvider(type,provider,pluginName){if(!this.providers.has(type))this.providers.set(type,new Map),this.providerOrder.set(type,[]);let typeMap=this.providers.get(type),order=this.providerOrder.get(type);if(typeMap.has(pluginName))getDaemonProfile().logger.info("service-registry",`Updating ${type} provider from plugin '${pluginName}'`);else getDaemonProfile().logger.info("service-registry",`Registered ${type} provider from plugin '${pluginName}'`);if(typeMap.set(pluginName,{provider,pluginName,registeredAt:new Date}),!order.includes(pluginName))order.push(pluginName)}getProvider(type){let typeMap=this.providers.get(type);if(!typeMap||typeMap.size===0)return;let defaultName=this.defaults.get(type);if(defaultName&&typeMap.has(defaultName))return typeMap.get(defaultName).provider;let order=this.providerOrder.get(type);if(order&&order.length>0){let firstPluginName=order[0];return typeMap.get(firstPluginName)?.provider}return}getProviderByName(type,pluginName){let typeMap=this.providers.get(type);if(!typeMap)return;return typeMap.get(pluginName)?.provider}hasProvider(type){let typeMap=this.providers.get(type);return!!typeMap&&typeMap.size>0}unregisterProvider(type,pluginName){let typeMap=this.providers.get(type);if(!typeMap)return!1;let removed=typeMap.delete(pluginName);if(removed){let order=this.providerOrder.get(type);if(order){let idx=order.indexOf(pluginName);if(idx!==-1)order.splice(idx,1)}this.clearProviderDefault(type,pluginName),getDaemonProfile().logger.info("service-registry",`Unregistered ${type} provider from plugin '${pluginName}'`)}return removed}unregisterPlugin(pluginName){for(let type of PROVIDER_TYPES)this.unregisterProvider(type,pluginName);this.unregisterServices(pluginName)}listProvidersForType(type){let typeMap=this.providers.get(type);if(!typeMap)return[];let defaultName=this.defaults.get(type);if(!defaultName||!typeMap.has(defaultName)){let order=this.providerOrder.get(type);defaultName=order&&order.length>0?order[0]:void 0}return Array.from(typeMap.keys()).map((name)=>({pluginName:name,isDefault:name===defaultName}))}registerService(pluginName,serviceName,service){let key=`${pluginName}:${serviceName}`;this.services.set(key,{service,pluginName}),getDaemonProfile().logger.debug("service-registry",`Registered service '${key}'`)}getService(pluginName,serviceName){let key=`${pluginName}:${serviceName}`;return this.services.get(key)?.service}unregisterServices(pluginName){let keysToDelete=[];for(let[key,entry]of this.services)if(entry.pluginName===pluginName)keysToDelete.push(key);for(let key of keysToDelete)this.services.delete(key);if(keysToDelete.length>0)getDaemonProfile().logger.info("service-registry",`Unregistered ${keysToDelete.length} services from ${pluginName}`)}listProviders(){let result=[];for(let[type,typeMap]of this.providers)for(let[pluginName,entry]of typeMap)result.push({type,name:pluginName,pluginName:entry.pluginName});return result}listServices(){return Array.from(this.services.entries()).map(([key,entry])=>({pluginName:entry.pluginName,serviceName:key.split(":").slice(1).join(":")}))}clear(){this.providers.clear(),this.providerOrder.clear(),this.defaults.clear(),this.services.clear()}}function pluginContractV2EnforceMode(){let v=process.env.VIBECONTROLS_PLUGIN_CONTRACT_V2_ENFORCE;return v==="1"||v==="true"||v==="on"}var FULL_TRUST_CAPS={storage:"rw",secrets:"rw",gateway:!0,broadcast:!0,subprocess:!0,audit:!0,telemetry:!0,singletonOnly:!1,requiresIsolation:!1},EMPTY_CAPS={storage:"none",secrets:"none",gateway:!1,broadcast:!1,subprocess:!1,audit:!1,telemetry:!1,singletonOnly:!1,requiresIsolation:!1};function strictCapsMode(){let v=process.env.VIBECONTROLS_STRICT_PLUGIN_CAPS;return v==="1"||v==="true"||v==="on"}function buildCapabilityProxy(hostServices,pluginName,caps,audit){return new Proxy(hostServices,{get(target,prop,receiver){if(prop==="storage")return guardedStorage(target.storage,caps.storage??"none",pluginName,audit);if(prop==="broadcast"&&!caps.broadcast)return refusalFn(pluginName,"broadcast",audit);if(prop==="workspaceQuery"&&!caps.gateway)return refusalFn(pluginName,"workspaceQuery",audit);if(prop==="getAgentRecordId"&&!caps.gateway)return refusalFn(pluginName,"getAgentRecordId",audit);if(prop==="getWorkspaceId"&&!caps.gateway)return refusalFn(pluginName,"getWorkspaceId",audit);if(prop==="telemetry"){if(!caps.telemetry)return;return Reflect.get(target,prop,receiver)}return Reflect.get(target,prop,receiver)}})}function guardedStorage(storage,mode,pluginName,audit){if(mode==="rw")return storage;return new Proxy(storage,{get(target,op,receiver){let opName=String(op),isWriter=opName==="set"||opName==="delete"||opName==="deleteAll";if(mode==="none"||mode==="read"&&isWriter)return(..._args)=>{throw audit("plugin.capability.denied",{pluginName,surface:"storage",op:opName,mode}),Error(`plugin '${pluginName}' lacks storage:${isWriter?"rw":"read"} capability (op=${opName})`)};return Reflect.get(target,op,receiver)}})}function refusalFn(pluginName,what,audit){return(..._args)=>{throw audit("plugin.capability.denied",{pluginName,op:what}),Error(`plugin '${pluginName}' lacks capability for ${what}`)}}import{Worker}from"worker_threads";import{fileURLToPath}from"url";import{randomUUID}from"crypto";function resolveRuntimeEntry(){let here=fileURLToPath(import.meta.url),sep=here.lastIndexOf("/"),dir=sep>=0?here.slice(0,sep):here,tsPath=`${dir}/plugin-worker-runtime.ts`,jsPath=`${dir}/plugin-worker-runtime.js`;if(here.endsWith(".ts"))return tsPath;return jsPath}class PluginWorkerHost{worker;pending=new Map;spec;initPromise;terminated=!1;constructor(spec){this.spec=spec}async start(){if(this.initPromise)return this.initPromise;return this.initPromise=this.spawn(),this.initPromise}async spawn(){let entry=this.spec.runtimeEntry??resolveRuntimeEntry(),worker=new Worker(entry,{env:process.env});this.worker=worker,worker.on("message",(msg)=>this.onMessage(msg)),worker.on("error",(err)=>this.onWorkerError(err instanceof Error?err:Error(String(err)))),worker.on("exit",(code)=>this.onWorkerExit(code)),await this.call("init",{modulePath:this.spec.modulePath,ctxStub:{name:this.spec.ctx.name,dataDir:this.spec.ctx.dataDir}})}async dispatch(method,params=[]){if(this.terminated)throw Error("PluginWorkerHost terminated");if(!this.worker)await this.start();return this.call("dispatch",{method,params})}call(op,args){if(!this.worker)return Promise.reject(Error("worker not spawned"));let id=randomUUID();return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject});try{this.worker?.postMessage({id,op,args})}catch(err){this.pending.delete(id),reject(err instanceof Error?err:Error(String(err)))}})}onMessage(msg){let pending=this.pending.get(msg.id);if(!pending)return;if(this.pending.delete(msg.id),msg.ok)pending.resolve(msg.result);else{let err=Error(msg.error?.message??"worker error");if(msg.error?.stack)err.stack=msg.error.stack;pending.reject(err)}}onWorkerError(err){for(let[,p]of this.pending)p.reject(err);this.pending.clear()}onWorkerExit(code){if(this.pending.size>0){let err=Error(`worker exited (code=${code}) with pending calls`);for(let[,p]of this.pending)p.reject(err);this.pending.clear()}this.terminated=!0}async terminate(){if(this.terminated=!0,!this.worker)return;try{await this.worker.terminate()}catch{}this.worker=void 0}}var CORE_PLUGINS=[{packageName:"@vibecontrols/vibe-plugin-storage",pluginName:"storage",label:"Storage facade",description:"Storage provider registry \u2014 owns AgentDatabase contract. Bundled with the agent; the storage meta lazy-loads the configured provider by name.",category:"storage",trusted:!0,isCore:!0,isDefault:!0,isCritical:!0,removable:!1},...["agent","task","config","git","plugin-mgr","log"].map((name)=>({packageName:`@vibecontrols/vibe-plugin-${name}`,pluginName:name,label:`${name} (built-in)`,description:`Built-in ${name} plugin bundled with the agent.`,category:"core",trusted:!0,isCore:!0,removable:!1}))],META_PLUGINS=[{packageName:"@vibecontrols/vibe-plugin-tunnel",pluginName:"tunnel",label:"Tunnel manager",description:"Owns TunnelProvider interface. Routes to a registered tunnel provider.",category:"tunnel",trusted:!0,isDefault:!0,isCritical:!0,cliCommand:"tunnel",apiPrefix:"/api/tunnel"},{packageName:"@vibecontrols/vibe-plugin-session-manager",pluginName:"session-manager",label:"Session manager",description:"Owns SessionProvider interface. Routes to a registered session backend.",category:"session",trusted:!0,isCore:!0,isDefault:!0,isCritical:!0,cliCommand:"session",apiPrefix:"/api/sessions"},{packageName:"@vibecontrols/vibe-plugin-ai",pluginName:"ai",label:"AI orchestration",description:"Owns AIAgentProvider interface. Prompts, contexts, sessions, dispatch, logs.",category:"ai",trusted:!0,isDefault:!0,isCritical:!0,cliCommand:"ai",apiPrefix:"/api/ai"},{packageName:"@vibecontrols/vibe-plugin-plan",pluginName:"plan",label:"Plan orchestration",description:"Owns PlanProvider interface. Routes plan sessions to a registered provider.",category:"plan",trusted:!0,isDefault:!0,cliCommand:"plan",apiPrefix:"/api/plan"},{packageName:"@vibecontrols/vibe-plugin-gitops",pluginName:"gitops",label:"GitOps orchestrator",description:"Owns GitOpsProvider interface. Routes repo / PR / CI / security queries to a registered provider.",category:"gitops",trusted:!0,isDefault:!0,cliCommand:"gitops",apiPrefix:"/api/gitops"},{packageName:"@vibecontrols/vibe-plugin-security",pluginName:"security",label:"Security lifecycle",description:"Security lifecycle orchestrator \u2014 owns /api/security and dispatches to per-stage providers.",category:"security",trusted:!0,isDefault:!0,cliCommand:"security",apiPrefix:"/api/security"}],AGENT_PLUGINS=[{packageName:"@vibecontrols/vibe-plugin-agent-backup",pluginName:"agent-backup",label:"Agent backup",description:"Backup and restore agent state.",category:"agent",trusted:!0,isDefault:!0}],PLUGIN_CATALOG=[...CORE_PLUGINS,...META_PLUGINS,...AGENT_PLUGINS],ABILITIES=[{key:"storage",label:"Storage",description:"Encrypted local state for the agent.",categories:["storage"],mandatory:!0,defaultSelected:!0},{key:"tunnel",label:"Tunnels",description:"Expose the agent for remote access + share links.",categories:["tunnel"],mandatory:!0,defaultSelected:!0},{key:"backup",label:"Backup",description:"Backup + restore agent state.",categories:["agent"],mandatory:!0,defaultSelected:!0},{key:"session",label:"Terminal sessions",description:"Long-lived terminal / AI agent sessions.",categories:["session"],mandatory:!1,defaultSelected:!0},{key:"ai",label:"AI orchestration",description:"AI agent harnesses, prompts + dispatch.",categories:["ai"],mandatory:!1,defaultSelected:!0},{key:"plan",label:"Plan review",description:"Review / approve AI-proposed plans.",categories:["plan"],mandatory:!1,defaultSelected:!0},{key:"gitops",label:"GitOps",description:"Repo / PR / CI / security integration.",categories:["gitops"],mandatory:!1,defaultSelected:!0},{key:"security",label:"Security",description:"Security scanning lifecycle.",categories:["security"],mandatory:!1,defaultSelected:!0}],CORE_PLUGIN_NAMES=PLUGIN_CATALOG.filter((p)=>p.isCore&&p.category==="core").map((p)=>p.pluginName),DEFAULT_PLUGINS=PLUGIN_CATALOG.filter((p)=>p.isDefault===!0);function getDefaultPluginsForPlatform(_platform){return DEFAULT_PLUGINS}function getDefaultPluginsForAbilities(platform,selectedAbilityKeys){let selected=selectedAbilityKeys instanceof Set?selectedAbilityKeys:new Set(selectedAbilityKeys),activeCategories=new Set;for(let ability of ABILITIES)if(ability.mandatory||selected.has(ability.key))for(let c of ability.categories)activeCategories.add(c);return getDefaultPluginsForPlatform(platform).filter((p)=>activeCategories.has(p.category))}var TRUSTED_PACKAGES=new Set(PLUGIN_CATALOG.filter((p)=>p.trusted).map((p)=>p.packageName));function getCriticalPluginNames(){let fromCatalog=PLUGIN_CATALOG.filter((p)=>p.isCritical).flatMap((p)=>[p.packageName,p.pluginName]),override=process.env.VIBECONTROLS_CRITICAL_PLUGINS;if(override){let parts=override.split(",").map((s)=>s.trim()).filter(Boolean);return new Set([...fromCatalog,...parts])}return new Set(fromCatalog)}function isCriticalPlugin(packageOrName){return getCriticalPluginNames().has(packageOrName)}var AVAILABLE_PLUGINS=PLUGIN_CATALOG.filter((p)=>!p.isCore||p.category!=="core");function getCatalogEntry(packageName){return PLUGIN_CATALOG.find((p)=>p.packageName===packageName)}function collectPluginBinaries(_pluginIdentifiers){return new Set}function isTrustedPackage(packageName){return packageName.startsWith("@vibecontrols/vibe-plugin-")||TRUSTED_PACKAGES.has(packageName)}function resolvePluginByShortName(input){let raw=input.trim();if(!raw)return;if(raw.startsWith("@vibecontrols/"))return PLUGIN_CATALOG.find((p)=>p.packageName===raw);if(raw.startsWith("vibe-plugin-"))return PLUGIN_CATALOG.find((p)=>p.packageName===`@vibecontrols/${raw}`);let byPluginName=PLUGIN_CATALOG.find((p)=>p.pluginName===raw);if(byPluginName)return byPluginName;return PLUGIN_CATALOG.find((p)=>p.packageName===`@vibecontrols/vibe-plugin-${raw}`)}function getCatalogEntry2(pluginName){return PLUGIN_CATALOG.find((entry)=>entry.pluginName===pluginName)}var REGISTRY_FILE_NAME="plugins.json",PLUGIN_PACKAGE_RE=/^@vibecontrols\/vibe-plugin-[a-z0-9][a-z0-9-]{0,80}$/,PLUGIN_NAME_RE=/^[a-z0-9][a-z0-9-]{0,63}$/,PLUGIN_API_PREFIX_RE=/^\/api\/[A-Za-z0-9][A-Za-z0-9/_-]{0,127}$/,PLUGIN_PUBLIC_PATH_RE=/^\/[A-Za-z0-9][A-Za-z0-9/_-]{0,159}$/,PLUGIN_PACKAGE_ROOT=Symbol.for("vibecontrols.pluginPackageRoot");function getPluginPackageRoot(plugin){return plugin[PLUGIN_PACKAGE_ROOT]}function validatePluginPackageName(packageName){let normalized=packageName.trim();if(!PLUGIN_PACKAGE_RE.test(normalized))throw Error("Plugin package must be a @vibecontrols/vibe-plugin-* package name.");if(!isTrustedPackage(normalized)&&process.env.VIBECONTROLS_ALLOW_UNTRUSTED_PLUGINS!=="1")throw Error("Plugin package is not in the trusted VibeControls catalog.");return normalized}function isPathInside(parent,child){let rel=relative(parent,child);return rel===""||!!rel&&!rel.startsWith("..")&&!isAbsolute(rel)}function getInstallMode(){let v=(process.env.VIBECONTROLS_PLUGIN_INSTALL_MODE||"").toLowerCase();if(v==="local")return"local";if(v==="global")return"global";return process.platform==="win32"?"local":"global"}var PROVIDER_DEFAULT_PREFIX="provider:default:";function isPluginEntry(value){if(!value||typeof value!=="object")return!1;let entry=value;return typeof entry.packageName==="string"&&typeof entry.version==="string"&&typeof entry.pluginName==="string"&&typeof entry.installedAt==="string"}var DEFAULT_PLUGINS2=DEFAULT_PLUGINS;class PluginManager{registry=[];loaded=new Map;corePlugins=new Map;db;forcedIsolation=new Set;workerHosts=new Map;constructor(db){this.db=db,this.ensureDir(),this.loadRegistry();let isolateEnv=process.env.VIBE_ISOLATE_PLUGINS;if(typeof isolateEnv==="string"&&isolateEnv.length>0)this.setForcedIsolation(isolateEnv.split(",").map((n)=>n.trim()).filter((n)=>n.length>0))}setForcedIsolation(names){this.forcedIsolation=new Set(names.filter((n)=>n.length>0))}shouldIsolate(plugin){if(this.forcedIsolation.has(plugin.name))return!0;return plugin.capabilities?.requiresIsolation===!0}getWorkerHost(pluginName){return this.workerHosts.get(pluginName)}registerWorkerHost(pluginName,host){this.workerHosts.set(pluginName,host)}auditSink=(event,payload)=>{getDaemonProfile().logger.warn("plugin-capability",event,payload)};static resolveEffectiveCapabilities(plugin,isCore){if(plugin.capabilities)return plugin.capabilities;return isCore?FULL_TRUST_CAPS:EMPTY_CAPS}wrapHostServices(plugin,hostServices){let caps=PluginManager.resolveEffectiveCapabilities(plugin,this.corePlugins.has(plugin.name));return buildCapabilityProxy(hostServices,plugin.name,caps,this.auditSink)}acceptCapabilities(plugin,source){if(plugin.capabilities?.singletonOnly===!0&&profileRegistry.size()>1)return getDaemonProfile().logger.error("plugin-manager",`[plugin] '${plugin.name}' refused to load: singletonOnly capability incompatible with multi-profile daemon (profiles=${profileRegistry.size()}, source=${source})`),!1;if(plugin.capabilities)return!0;if(strictCapsMode())return getDaemonProfile().logger.error("plugin-manager",`[plugin] '${plugin.name}' refused to load: missing required capabilities declaration (source=${source})`),!1;if(source.startsWith("core:"))getDaemonProfile().logger.warn("plugin-manager",`[plugin] '${plugin.name}' loaded without a capabilities declaration \u2014 granting full trust (core plugin). Declare capabilities explicitly. (source=${source})`);else getDaemonProfile().logger.warn("plugin-manager",`[plugin] '${plugin.name}' loaded without a capabilities declaration \u2014 defaulting to EMPTY capability set (deny by default). External plugins MUST declare capabilities explicitly to access host services. (source=${source})`);return!0}ensureDir(){let dir=getVibecontrolsDir();if(!existsSync(dir))mkdirSync(dir,{recursive:!0})}loadRegistry(){let registryFile=join(getVibecontrolsDir(),REGISTRY_FILE_NAME);try{if(existsSync(registryFile)){let raw=readFileSync(registryFile,"utf-8"),parsed=JSON.parse(raw);if(Array.isArray(parsed)&&parsed.every(isPluginEntry))this.registry=parsed.filter((entry)=>{try{return validatePluginPackageName(entry.packageName),!0}catch(err){return getDaemonProfile().logger.warn("plugin-manager","Ignoring invalid plugin entry",{packageName:entry.packageName,error:String(err)}),!1}});else throw Error("plugins.json has an invalid schema")}}catch(err){getDaemonProfile().logger.warn("plugin-manager","Failed to load plugin registry",{path:registryFile,error:String(err)}),this.registry=[]}}saveRegistry(){this.ensureDir();let registryFile=join(getVibecontrolsDir(),REGISTRY_FILE_NAME),tmpFile=`${registryFile}.${process.pid}.tmp`;writeFileSync(tmpFile,`${JSON.stringify(this.registry,null,2)}
|
|
3
|
-
`,{encoding:"utf-8",mode:384}),renameSync(tmpFile,registryFile)}async loadCorePlugins(){let coreModules=await Promise.allSettled([import("./index-
|
|
4
|
-
`);if(Bun.spawnSync(["bun","add",installSpec,"--registry",registry],{cwd:localDir,timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode!==0){let npmResult=Bun.spawnSync(["npm","install",installSpec,"--registry",registry],{cwd:localDir,timeout:120000,stdout:"pipe",stderr:"pipe"});if(npmResult.exitCode!==0)throw Error(npmResult.stderr.toString().trim())}}else if(Bun.spawnSync(["bun","install","-g",installSpec,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode!==0){let npmResult=Bun.spawnSync(["npm","install","-g",installSpec,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"});if(npmResult.exitCode!==0)throw Error(npmResult.stderr.toString().trim())}this.pluginRoots=void 0}catch(err){let message=err instanceof Error?err.message:String(err);if(refreshLatest)try{await import(packageName),getDaemonProfile().logger.warn("plugin-manager",`Could not refresh ${packageName} to @latest (${message}); using the already-installed copy`),this.pluginRoots=void 0;let plugin2=await this.importPlugin(packageName),entry2={packageName,version:plugin2.version,pluginName:plugin2.name,installedAt:new Date().toISOString()};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry2),this.saveRegistry(),entry2}catch{}throw Error(`Failed to install ${packageName}: ${message}`,{cause:err})}let plugin=await this.importPlugin(packageName);if(refreshLatest&&concreteLatest&&plugin.version!==concreteLatest)getDaemonProfile().logger.warn("plugin-manager",`${packageName} refreshed to ${plugin.version} but registry latest is ${concreteLatest} \u2014 a stale resolver/cache may still be in play`);if(this.corePlugins.has(plugin.name))getDaemonProfile().logger.info("plugin-manager",`External plugin '${packageName}' overrides core plugin '${plugin.name}'`);let entry={packageName,version:plugin.version,pluginName:plugin.name,installedAt:new Date().toISOString()};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry),this.saveRegistry(),getDaemonProfile().logger.info("plugin-manager",`Installed ${packageName}@${plugin.version} (${plugin.name})`),entry}async ensureDefaultPlugins(onStatus,skipPlugins=[],selectedAbilities){let installed=[],skipSet=new Set(skipPlugins),entries=selectedAbilities?getDefaultPluginsForAbilities(process.platform,selectedAbilities):getDefaultPluginsForPlatform(process.platform);for(let entry of entries){if(skipSet.has(entry.packageName)||skipSet.has(entry.pluginName)){getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} skipped by --skip-plugin`);continue}if(entry.isCore){getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} is bundled with the agent \u2014 skipping auto-install`);continue}if(this.registry.some((r)=>r.packageName===entry.packageName)){getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} already installed, skipping auto-install`);continue}onStatus?.(`Installing ${entry.label} (${entry.packageName})...`);try{await this.install(entry.packageName),installed.push(entry),onStatus?.(`Installed ${entry.label} (${entry.packageName})`)}catch(err){let message=err instanceof Error?err.message:String(err);getDaemonProfile().logger.error("plugin-manager",`Failed to auto-install ${entry.packageName}: ${message}`),onStatus?.(`Failed to install ${entry.label}: ${message}`)}}if(installed.length>0)this.loadRegistry();return installed}async installAndLoad(packageName,app,hostServices){let entry=await this.install(packageName),plugin=this.loaded.get(entry.packageName);if(plugin){this.registerPluginProviders(plugin,hostServices);let wrapped=this.wrapHostServices(plugin,hostServices);if(plugin.onServerStart)await plugin.onServerStart(app,wrapped);if(plugin.onServerReady)await plugin.onServerReady(app,wrapped)}return entry}async update(packageName){packageName=validatePluginPackageName(packageName),getDaemonProfile().logger.info("plugin-manager",`Updating ${packageName}...`);let existing=this.registry.find((e)=>e.packageName===packageName);if(!existing)throw Error(`Plugin ${packageName} is not installed. Use 'install' first.`);let registry=getPluginRegistry();try{if(Bun.spawnSync(["bun","install","-g",`${packageName}@latest`,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode!==0){let npmResult=Bun.spawnSync(["npm","install","-g",`${packageName}@latest`,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"});if(npmResult.exitCode!==0)throw Error(npmResult.stderr.toString().trim())}}catch(err){let message=err instanceof Error?err.message:String(err);throw Error(`Failed to update ${packageName}: ${message}`,{cause:err})}this.loaded.delete(packageName);let plugin=await this.importPlugin(packageName),entry={packageName,version:plugin.version,pluginName:plugin.name,installedAt:existing.installedAt};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry),this.saveRegistry(),getDaemonProfile().logger.info("plugin-manager",`Updated ${packageName} to v${plugin.version}`),entry}async updateAll(){let results=[];for(let entry of[...this.registry])try{let updated=await this.update(entry.packageName);results.push({packageName:entry.packageName,success:!0,version:updated.version})}catch(err){results.push({packageName:entry.packageName,success:!1,error:err instanceof Error?err.message:String(err)})}return results}async removeAll(hostServices){let results=[],toRemove=[...this.registry];for(let entry of toRemove)try{await this.remove(entry.packageName,hostServices),results.push({packageName:entry.packageName,success:!0})}catch(err){results.push({packageName:entry.packageName,success:!1,error:err instanceof Error?err.message:String(err)})}return results}async remove(packageName,hostServices){packageName=validatePluginPackageName(packageName),getDaemonProfile().logger.info("plugin-manager",`Removing ${packageName}...`);let plugin=this.loaded.get(packageName);if(plugin?.onServerStop)try{await plugin.onServerStop()}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Error stopping ${packageName}: ${err}`)}if(plugin)hostServices?.serviceRegistry.unregisterPlugin(plugin.name),this.loaded.delete(packageName);let registryEntry=this.registry.find((e)=>e.packageName===packageName);if(!plugin&®istryEntry)hostServices?.serviceRegistry.unregisterPlugin(registryEntry.pluginName);let localDir=join(getVibecontrolsDir(),"agent-plugins");try{if(existsSync(join(localDir,"package.json")))Bun.spawnSync(["bun","remove",packageName],{cwd:localDir,timeout:60000,stdout:"pipe",stderr:"pipe"})}catch{}try{Bun.spawnSync(["bun","remove","-g",packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"})}catch{try{Bun.spawnSync(["npm","uninstall","-g",packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"})}catch{}}this.pluginRoots=void 0,this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.saveRegistry(),getDaemonProfile().logger.info("plugin-manager",`Removed ${packageName}`)}async loadAll(){this.loaded.clear();let stale=[];for(let entry of this.registry)try{validatePluginPackageName(entry.packageName),await this.importPlugin(entry.packageName),getDaemonProfile().logger.info("plugin-manager",`Loaded ${entry.packageName} (${entry.pluginName})`)}catch(err){let code=err.code;if(code==="ENOENT_PLUGIN")getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} not present on disk \u2014 dropping from registry`),stale.push(entry.packageName);else if(code==="EPLUGIN_LEGACY_SINGLETON")getDaemonProfile().logger.warn("plugin-manager",`Skipping ${entry.packageName}: ${err instanceof Error?err.message:String(err)}`);else getDaemonProfile().logger.warn("plugin-manager",`Failed to load ${entry.packageName} \u2014 dropping from registry: ${err}`),stale.push(entry.packageName)}if(stale.length>0){this.registry=this.registry.filter((e)=>!stale.includes(e.packageName));try{this.saveRegistry()}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to persist pruned registry: ${err}`)}}}async importPlugin(packageName){packageName=validatePluginPackageName(packageName);let mod={},packageRoot;try{packageRoot=this.resolvePackageRoot(packageName),mod=await import(packageName)}catch{let roots=this.getGlobalRoots(),loaded=!1,attempts=[],allMissing=!0;for(let root of roots){let absolutePath=join(root,packageName);if(!existsSync(absolutePath)){attempts.push({path:absolutePath,error:"directory missing"});continue}allMissing=!1;try{packageRoot=absolutePath,mod=await import(absolutePath),loaded=!0;break}catch(err){attempts.push({path:absolutePath,error:err instanceof Error?err.message:String(err)})}try{let pkgJsonPath=join(absolutePath,"package.json");if(!existsSync(pkgJsonPath)){attempts.push({path:pkgJsonPath,error:"package.json missing"});continue}let pkgJson=JSON.parse(readFileSync(pkgJsonPath,"utf8")),entry=pkgJson.module??pkgJson.main??"./index.js",filePath=join(absolutePath,entry);packageRoot=absolutePath,mod=await import(filePath),loaded=!0;break}catch(err){attempts.push({path:absolutePath,error:err instanceof Error?err.message:String(err)})}}if(!loaded){let detail=attempts.map((a)=>`${a.path} (${a.error})`).join("; "),err=Error(`Cannot find module '${packageName}': bare import failed and global root attempts failed: ${detail||"(no roots probed)"}`);if(allMissing&&roots.length>0)err.code="ENOENT_PLUGIN";throw err}}let plugin=this.extractPlugin(mod,`external:${packageName}`,getDaemonProfile());if(!plugin){if(this.hasLegacySingleton(mod)){let err=Error(`${packageName} uses removed singleton contract; upgrade to createPlugin(ctx) factory (set VIBECONTROLS_PLUGIN_CONTRACT_V2_ENFORCE=1 to refuse strictly)`);throw err.code="EPLUGIN_LEGACY_SINGLETON",err}throw Error(`${packageName} does not export a valid createPlugin(ctx) factory`)}if(this.validatePluginExport(plugin,packageName,packageRoot),!this.acceptCapabilities(plugin,`external:${packageName}`))throw Error(`${packageName} refused to load: missing required capabilities declaration`);if(packageRoot)Object.defineProperty(plugin,PLUGIN_PACKAGE_ROOT,{value:packageRoot,enumerable:!1,configurable:!1});if(this.loaded.set(packageName,plugin),this.shouldIsolate(plugin))try{let ctx=getDaemonProfile(),host=new PluginWorkerHost({modulePath:packageName,pluginName:plugin.name,ctx:{name:ctx.name,dataDir:ctx.dataDir}});await host.start(),this.workerHosts.set(plugin.name,host),ctx.logger.info("plugin-manager",`Isolated plugin '${plugin.name}' running in worker_threads`)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to isolate plugin '${plugin.name}'; falling back to in-host: ${err instanceof Error?err.message:String(err)}`)}return plugin}resolvePackageRoot(packageName){let meta=import.meta;if(typeof meta.resolve!=="function")return;try{let resolved=meta.resolve(packageName);if(!resolved.startsWith("file:"))return;let current=dirname(fileURLToPath2(resolved));while(current&¤t!==dirname(current)){if(existsSync(join(current,"package.json")))return current;current=dirname(current)}}catch{return}return}validatePluginExport(plugin,packageName,packageRoot){if(!PLUGIN_NAME_RE.test(plugin.name))throw Error(`${packageName} exports an invalid plugin name`);if(!plugin.version||plugin.version.length>64)throw Error(`${packageName} exports an invalid plugin version`);if(plugin.description&&plugin.description.length>500)throw Error(`${packageName} description is too long`);if(plugin.cliCommand&&!PLUGIN_NAME_RE.test(plugin.cliCommand))throw Error(`${packageName} exports an invalid CLI command`);if(plugin.apiPrefix){if(!PLUGIN_API_PREFIX_RE.test(plugin.apiPrefix)||plugin.apiPrefix.includes("..")||plugin.apiPrefix.includes("//"))throw Error(`${packageName} exports an invalid API prefix`)}if(plugin.publicPaths){if(plugin.publicPaths.length>20)throw Error(`${packageName} exports too many public paths`);for(let publicPath of plugin.publicPaths)if(!PLUGIN_PUBLIC_PATH_RE.test(publicPath)||publicPath.includes("..")||publicPath.includes("//"))throw Error(`${packageName} exports an invalid public path`)}if(plugin.dependencies&&plugin.dependencies.length>20)throw Error(`${packageName} exports too many dependencies`);if(plugin.tags){let allowedTags=new Set(["backend","frontend","cli","provider","adapter","integration"]);if(plugin.tags.length>allowedTags.size)throw Error(`${packageName} exports too many tags`);for(let tag of plugin.tags)if(!allowedTags.has(tag))throw Error(`${packageName} exports an invalid tag`)}if(plugin.ui){if(plugin.ui.title&&plugin.ui.title.length>120)throw Error(`${packageName} UI title is too long`);if(plugin.ui.staticDir){if(!packageRoot)throw Error(`${packageName} UI staticDir requires a package root`);let rootReal=realpathSync(packageRoot),staticReal=realpathSync(plugin.ui.staticDir);if(!statSync(staticReal).isDirectory()||!isPathInside(rootReal,staticReal))throw Error(`${packageName} UI staticDir must be inside the plugin package`)}}}pluginRoots;getPluginRoots(){if(this.pluginRoots!==void 0)return this.pluginRoots;let roots=[],seen=new Set,push=(p)=>{if(!p)return;if(seen.has(p))return;if(!existsSync(p))return;seen.add(p),roots.push(p)};try{let localDir=join(getVibecontrolsDir(),"agent-plugins","node_modules");push(localDir)}catch{}let extra=process.env.VIBECONTROLS_PLUGIN_ROOTS;if(extra){let rootsDelimiter=process.platform==="win32"?";":":";for(let p of extra.split(rootsDelimiter).map((s)=>s.trim()).filter(Boolean))push(p)}try{let dir=process.cwd(),root=dirname(dir)===dir?dir:"/";while(dir&&dir!==root&&dir!==dirname(dir))push(join(dir,"node_modules")),dir=dirname(dir)}catch{}try{let dir=dirname(fileURLToPath2(import.meta.url)),stop=dirname(dir)===dir?dir:"/";while(dir&&dir!==stop&&dir!==dirname(dir)){if(existsSync(join(dir,"package.json"))){push(join(dir,"node_modules"));break}dir=dirname(dir)}}catch{}try{push(join(osHomedir(),".bun","install","global","node_modules"))}catch{}try{let result=Bun.spawnSync(["npm","root","-g"],{stdout:"pipe",stderr:"pipe",timeout:1e4});if(result.exitCode===0)push(result.stdout.toString().trim())}catch{}return this.pluginRoots=roots,roots}getGlobalRoots(){return this.getPluginRoots()}getNpmGlobalRoot(){let roots=this.getPluginRoots();return roots.length>0?roots[0]:null}extractPlugin(mod,label,ctx){let factory=this.findFactory(mod);if(factory){let plugin=factory(ctx);if(!this.isVibePlugin(plugin))throw Error(`plugin '${label}' createPlugin(ctx) factory returned an invalid VibePlugin`);return plugin}if(this.hasLegacySingleton(mod)){let message=`plugin '${label}' uses removed singleton contract; upgrade to createPlugin(ctx) factory`;if(pluginContractV2EnforceMode())throw Error(message);getDaemonProfile().logger.warn("plugin-manager",message);return}return}findFactory(mod){if(typeof mod.createPlugin==="function")return mod.createPlugin;let defaultExport=mod.default;if(defaultExport&&typeof defaultExport.createPlugin==="function")return defaultExport.createPlugin;return}hasLegacySingleton(mod){if(this.isVibePlugin(mod.vibePlugin))return!0;let defaultExport=mod.default;if(defaultExport&&this.isVibePlugin(defaultExport.vibePlugin))return!0;if(this.isVibePlugin(defaultExport))return!0;return!1}isVibePlugin(value){if(!value||typeof value!=="object")return!1;let candidate=value;return typeof candidate.name==="string"&&typeof candidate.version==="string"}registerProviders(plugin,hostServices){this.registerPluginProviders(plugin,hostServices)}registerPluginProviders(plugin,hostServices){let isDefault=getCatalogEntry2(plugin.name)?.isDefault===!0,pinDefault=(type)=>{if(!isDefault)return;hostServices.serviceRegistry.setProviderDefault(type,plugin.name)};if(plugin.providers?.tunnel){if(hostServices.serviceRegistry.registerProvider("tunnel",plugin.providers.tunnel,plugin.name),pinDefault("tunnel"),plugin.tunnelDomainSuffixes)for(let suffix of plugin.tunnelDomainSuffixes)registerAllowedTunnelSuffix(suffix)}if(plugin.providers?.session)hostServices.serviceRegistry.registerProvider("session",plugin.providers.session,plugin.name),pinDefault("session");if(plugin.providers?.ai)hostServices.serviceRegistry.registerProvider("ai",plugin.providers.ai,plugin.name),pinDefault("ai")}async dispatchCliSetup(program,hostServices){for(let plugin of this.getAllPlugins())if(plugin.onCliSetup)try{await plugin.onCliSetup(program,this.wrapHostServices(plugin,hostServices))}catch(err){getDaemonProfile().logger.warn("plugin-manager",`onCliSetup failed for ${plugin.name}: ${err}`)}}async dispatchServerStart(app,hostServices){let sorted=this.sortAllByDependencies(),failures=[];for(let plugin of sorted){if(this.registerPluginProviders(plugin,hostServices),plugin.onServerStart){let startedAt=Date.now();try{await plugin.onServerStart(app,this.wrapHostServices(plugin,hostServices)),(async()=>{try{let{telemetryService}=await import("./telemetry-rew0mtj2.js");telemetryService.emit("plugin.activated",{plugin_name:plugin.name,version:plugin.version,duration_ms:Date.now()-startedAt})}catch{}})()}catch(err){let message=err instanceof Error?err.message:String(err);getDaemonProfile().logger.error("plugin-manager",`onServerStart failed for ${plugin.name}: ${message}`),(async()=>{try{let{telemetryService}=await import("./telemetry-rew0mtj2.js");telemetryService.emit("plugin.failed",{plugin_name:plugin.name,error_class:err instanceof Error?err.constructor.name:"Error"})}catch{}})();let entry=this.registry.find((e)=>e.pluginName===plugin.name);failures.push({plugin:plugin.name,packageName:entry?.packageName,error:message})}}this.registerPluginProviders(plugin,hostServices)}return{failures}}async dispatchServerReady(app,hostServices){for(let plugin of this.getAllPlugins())if(plugin.onServerReady)try{await plugin.onServerReady(app,this.wrapHostServices(plugin,hostServices))}catch(err){getDaemonProfile().logger.warn("plugin-manager",`onServerReady failed for ${plugin.name}: ${err}`)}}async provisionDefaultPrereqs(app,hostServices){let{runPluginPrereqs}=await import("./prereqs-runner-k85knvak.js"),skipInstall=process.env.VIBE_SKIP_PREREQ_INSTALL==="1",platform=process.platform,newlyInstalled=[],metaPkgs=getDefaultPluginsForPlatform(process.platform).map((p)=>p.packageName);if(!skipInstall)for(let pkg of metaPkgs){if(this.loaded.has(pkg))continue;try{getDaemonProfile().logger.info("plugin-manager",`Installing default meta ${pkg}`),await this.install(pkg,{refreshLatest:!0}),newlyInstalled.push(pkg)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to install default meta ${pkg}: ${err}`)}}let providerPkgs=new Set;for(let[metaPkg,metaPlugin]of this.getAllLoaded()){let mps=metaPlugin.metaProviders??[];if(mps.length>0)getDaemonProfile().logger.info("plugin-manager",`Meta ${metaPkg} declares ${mps.length} provider(s) (platform=${platform})`);for(let prov of mps)if(prov.defaultOn?.includes(platform))providerPkgs.add(prov.packageName)}if(getDaemonProfile().logger.info("plugin-manager",`Default providers for ${platform}: ${[...providerPkgs].join(", ")||"(none)"}`),!skipInstall)for(let pkg of providerPkgs){if(this.loaded.has(pkg))continue;try{getDaemonProfile().logger.info("plugin-manager",`Installing default provider ${pkg} (declared by a meta plugin)`),await this.install(pkg,{refreshLatest:!0}),newlyInstalled.push(pkg)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to install default provider ${pkg}: ${err}`)}}let defaults=new Set([...metaPkgs,...providerPkgs]);for(let[packageName,plugin]of this.getAllLoaded()){if(!defaults.has(packageName))continue;try{let r=await runPluginPrereqs(plugin,packageName,app,this.db??null,{skipInstall});if(!r.noProtocol&&!r.satisfied){let pending=r.pendingSudo.map((p)=>p.name).join(", ")||"none";getDaemonProfile().logger.warn("plugin-manager",`${packageName}: prerequisites still unsatisfied after install (pendingSudo: ${pending})`)}}catch(err){getDaemonProfile().logger.warn("plugin-manager",`${packageName}: prereq provisioning failed: ${err}`)}}for(let pkg of newlyInstalled){let plugin=this.loaded.get(pkg);if(!plugin)continue;try{this.registerPluginProviders(plugin,hostServices);let wrapped=this.wrapHostServices(plugin,hostServices);if(plugin.onServerStart)await plugin.onServerStart(app,wrapped);if(plugin.onServerReady)await plugin.onServerReady(app,wrapped)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`${pkg}: provider start failed: ${err}`)}}await this.electPlatformDefaultProviders(hostServices,platform)}inferProviderTypeForPlugin(hostServices,pluginName){for(let type of PROVIDER_TYPES)if(hostServices.serviceRegistry.listProvidersForType(type).some((e)=>e.pluginName===pluginName))return type;return}async electPlatformDefaultProviders(hostServices,platform){for(let[,metaPlugin]of this.getAllLoaded())for(let prov of metaPlugin.metaProviders??[]){if(!prov.pluginName)continue;if(!prov.defaultOn?.includes(platform))continue;let type=prov.providerType??this.inferProviderTypeForPlugin(hostServices,prov.pluginName);if(!type)continue;if(!hostServices.serviceRegistry.listProvidersForType(type).some((e)=>e.pluginName===prov.pluginName))continue;try{let existing=await this.getDefaultProvider(type);if(existing&&existing!==prov.pluginName)continue;if(hostServices.serviceRegistry.setProviderDefault(type,prov.pluginName),!existing)await this.setDefaultProvider(type,prov.pluginName).catch(()=>{});getDaemonProfile().logger.info("plugin-manager",`Elected platform-default '${type}' provider '${prov.pluginName}' for ${platform}`)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to elect default '${type}' provider '${prov.pluginName}': ${err}`)}}}async dispatchServerStop(context){for(let plugin of this.getAllPlugins())if(plugin.onServerStop)try{await plugin.onServerStop(context)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`onServerStop failed for ${plugin.name}: ${err}`)}}async runPluginNuke(packageName,hostServices,ctx){let targets=packageName?this.loaded.has(packageName)?[[packageName,this.loaded.get(packageName)]]:[]:Array.from(this.loaded.entries()),results=[];for(let[pkg,plugin]of targets){if(!plugin.onNuke)continue;try{let wrapped=this.wrapHostServices(plugin,hostServices),out=await plugin.onNuke(wrapped,ctx);results.push({plugin:pkg,ok:!0,reaped:out?.reaped??[],notes:out?.notes??[]}),getDaemonProfile().logger.info("plugin-manager",`onNuke ${ctx.dryRun?"(dry-run) ":""}completed for ${pkg}`,{reaped:out?.reaped??[]})}catch(err){let msg=err instanceof Error?err.message:String(err);results.push({plugin:pkg,ok:!1,reaped:[],notes:[],error:msg}),getDaemonProfile().logger.warn("plugin-manager",`onNuke failed for ${pkg}: ${msg}`)}}return{results}}async reloadAll(app,hostServices){await this.dispatchServerStop({reason:"reload"});for(let plugin of this.loaded.values())hostServices.serviceRegistry.unregisterPlugin(plugin.name);this.loaded.clear(),this.corePlugins.clear(),this.loadRegistry(),await this.loadCorePlugins(),await this.loadAll(),await this.dispatchServerStart(app,hostServices),await this.dispatchServerReady(app,hostServices)}sortAllByDependencies(){let allPlugins=this.getAllPlugins(),pluginMap=new Map;for(let plugin of allPlugins)pluginMap.set(plugin.name,plugin);let visited=new Set,visiting=new Set,sorted=[],visit=(name)=>{if(visited.has(name))return;if(visiting.has(name))throw Error(`Plugin dependency cycle detected at '${name}'`);let plugin=pluginMap.get(name);if(!plugin)return;if(visiting.add(name),plugin.dependencies)for(let dep of plugin.dependencies){if(!pluginMap.has(dep)){getDaemonProfile().logger.warn("plugin-manager",`Plugin '${plugin.name}' depends on missing plugin '${dep}'`);continue}visit(dep)}visiting.delete(name),visited.add(name),sorted.push(plugin)};for(let plugin of allPlugins)visit(plugin.name);return sorted}sortByDependencies(){let entries=Array.from(this.loaded.entries()),nameToPackage=new Map;for(let[pkg,plugin]of entries)nameToPackage.set(plugin.name,pkg);let visited=new Set,sorted=[],visit=(pkg)=>{if(visited.has(pkg))return;visited.add(pkg);let plugin=this.loaded.get(pkg);if(plugin?.dependencies)for(let dep of plugin.dependencies){let depPkg=nameToPackage.get(dep);if(depPkg&&this.loaded.has(depPkg))visit(depPkg)}let loadedPlugin=this.loaded.get(pkg);if(loadedPlugin)sorted.push([pkg,loadedPlugin])};for(let[pkg]of entries)visit(pkg);return sorted}getPluginByKey(key){for(let[,plugin]of this.loaded)if(plugin.name===key)return plugin;return this.corePlugins.get(key)}getAllPlugins(){let merged=new Map;for(let[key,plugin]of this.corePlugins)merged.set(key,plugin);for(let[,plugin]of this.loaded)merged.set(plugin.name,plugin);return Array.from(merged.values())}getAllPluginsByTag(tag){return this.getAllPlugins().filter((plugin)=>plugin.tags?.includes(tag)??!1)}resolvePluginChain(tokens){let allKeys=new Set;for(let p of this.getAllPlugins())allKeys.add(p.name);let chain=[],commandIdx=-1;for(let i=0;i<tokens.length;i++){if(tokens[i].startsWith("-")){commandIdx=i;break}if(allKeys.has(tokens[i]))chain.push(tokens[i]);else{commandIdx=i;break}}if(commandIdx===-1)return{chain,command:"",args:[]};return{chain,command:tokens[commandIdx],args:tokens.slice(commandIdx+1)}}async getDefaultProvider(type){if(!this.db)return null;return await this.db.getConfig(`${PROVIDER_DEFAULT_PREFIX}${type}`)??null}async setDefaultProvider(type,pluginName){if(!this.db)throw Error("Cannot set default provider: PluginManager was constructed without a database reference");if(!this.getPluginByKey(pluginName))throw Error(`Plugin '${pluginName}' not found`);await this.db.setConfig(`${PROVIDER_DEFAULT_PREFIX}${type}`,pluginName),getDaemonProfile().logger.info("plugin-manager",`Set default '${type}' provider to '${pluginName}'`)}getPluginDetails(){let coreInfos=this.getCorePluginDetails(),externalInfos=this.getExternalPluginDetails(),merged=new Map;for(let info of coreInfos)merged.set(info.pluginName,info);for(let info of externalInfos)merged.set(info.pluginName,info);return Array.from(merged.values())}getCorePluginDetails(){let infos=[];for(let[name,plugin]of this.corePlugins)infos.push({packageName:`@vibecontrols/plugin-${name}`,pluginName:name,version:plugin.version,description:plugin.description,tags:plugin.tags,cliCommand:plugin.cliCommand,apiPrefix:plugin.apiPrefix,dependencies:plugin.dependencies,installedAt:"",loaded:!0,isCore:!0,hasUI:!!plugin.ui,uiUrl:plugin.ui?`/ui/${name}`:void 0,hasCliSetup:!!plugin.onCliSetup,hasServerStart:!!plugin.onServerStart,hasServerStop:!!plugin.onServerStop,hasServerReady:!!plugin.onServerReady,hasProviders:!!(plugin.providers?.tunnel||plugin.providers?.session||plugin.providers?.ai)});return infos}getExternalPluginDetails(){return this.registry.map((entry)=>{let plugin=this.loaded.get(entry.packageName),catalogEntry=getCatalogEntry2(entry.pluginName);return{packageName:entry.packageName,pluginName:entry.pluginName,version:plugin?.version??entry.version,description:plugin?.description,tags:plugin?.tags,cliCommand:plugin?.cliCommand,apiPrefix:plugin?.apiPrefix,dependencies:plugin?.dependencies,installedAt:entry.installedAt,loaded:!!plugin,isCore:catalogEntry?.isCore===!0,hasUI:!!plugin?.ui,uiUrl:plugin?.ui?`/ui/${entry.pluginName}`:void 0,hasCliSetup:!!plugin?.onCliSetup,hasServerStart:!!plugin?.onServerStart,hasServerStop:!!plugin?.onServerStop,hasServerReady:!!plugin?.onServerReady,hasProviders:!!(plugin?.providers?.tunnel||plugin?.providers?.session||plugin?.providers?.ai)}})}getRegistry(){return[...this.registry]}getLoaded(packageName){return this.loaded.get(packageName)}getAllLoaded(){return new Map(this.loaded)}isInstalled(packageName){return this.registry.some((e)=>e.packageName===packageName)}isLoaded(packageName){return this.loaded.has(packageName)}}
|
|
3
|
+
`,{encoding:"utf-8",mode:384}),renameSync(tmpFile,registryFile)}async loadCorePlugins(){let coreModules=await Promise.allSettled([import("./index-ftx08bfa.js"),import("./index-fwd90spw.js"),import("./index-m483e746.js"),import("./index-e875a6zy.js"),import("./index-pne5682j.js"),import("./index-4ymjceh1.js")]),ctx=getDaemonProfile();for(let i=0;i<coreModules.length;i++){let result=coreModules[i],expectedName=CORE_PLUGIN_NAMES[i];if(result.status==="fulfilled"){let mod=result.value,plugin;try{plugin=this.extractPlugin(mod,`core:${expectedName}`,ctx)}catch(err){getDaemonProfile().logger.error("plugin-manager",`Core plugin '${expectedName}' refused to load: ${err instanceof Error?err.message:String(err)}`);continue}if(plugin?.name){if(!this.acceptCapabilities(plugin,`core:${expectedName}`))continue;this.corePlugins.set(plugin.name,plugin),getDaemonProfile().logger.info("plugin-manager",`Core plugin loaded: ${plugin.name} v${plugin.version}`)}else getDaemonProfile().logger.warn("plugin-manager",`Core plugin '${expectedName}' did not export a valid createPlugin(ctx) factory`)}else getDaemonProfile().logger.warn("plugin-manager",`Failed to load core plugin '${expectedName}': ${result.reason}`)}getDaemonProfile().logger.info("plugin-manager",`Core plugins loaded: ${this.corePlugins.size}/${CORE_PLUGIN_NAMES.length}`)}async resolveConcreteLatest(packageName,registry){try{let url=`${registry.replace(/\/+$/,"")}/${packageName.replace("/","%2F")}`,res=await fetch(url,{headers:{accept:"application/json"},signal:AbortSignal.timeout(20000)});if(!res.ok)return;let latest=(await res.json())["dist-tags"]?.latest;if(typeof latest==="string"&&/^\d+\.\d+\.\d+/.test(latest))return latest}catch{}return}async install(packageName,opts={}){packageName=validatePluginPackageName(packageName);let refreshLatest=opts.refreshLatest===!0;if(getDaemonProfile().logger.info("plugin-manager",`Installing ${packageName}${refreshLatest?"@latest (refresh)":""}...`),!refreshLatest)try{await import(packageName),getDaemonProfile().logger.info("plugin-manager",`${packageName} already resolvable \u2014 skipping global install`);let plugin2=await this.importPlugin(packageName),entry2={packageName,version:plugin2.version,pluginName:plugin2.name,installedAt:new Date().toISOString()};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry2),this.saveRegistry(),entry2}catch{}let registry=getPluginRegistry(),installMode=getInstallMode(),concreteLatest=refreshLatest?await this.resolveConcreteLatest(packageName,registry):void 0,installSpec=refreshLatest?`${packageName}@${concreteLatest??"latest"}`:packageName;try{if(installMode==="local"){let localDir=join(getVibecontrolsDir(),"agent-plugins");if(!existsSync(localDir))mkdirSync(localDir,{recursive:!0});let pkgJsonPath=join(localDir,"package.json");if(!existsSync(pkgJsonPath))writeFileSync(pkgJsonPath,JSON.stringify({name:"vibecontrols-agent-plugins",private:!0,version:"0.0.0",dependencies:{}},null,2)+`
|
|
4
|
+
`);if(Bun.spawnSync(["bun","add",installSpec,"--registry",registry],{cwd:localDir,timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode!==0){let npmResult=Bun.spawnSync(["npm","install",installSpec,"--registry",registry],{cwd:localDir,timeout:120000,stdout:"pipe",stderr:"pipe"});if(npmResult.exitCode!==0)throw Error(npmResult.stderr.toString().trim())}}else if(Bun.spawnSync(["bun","install","-g",installSpec,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode!==0){let npmResult=Bun.spawnSync(["npm","install","-g",installSpec,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"});if(npmResult.exitCode!==0)throw Error(npmResult.stderr.toString().trim())}this.pluginRoots=void 0}catch(err){let message=err instanceof Error?err.message:String(err);if(refreshLatest)try{await import(packageName),getDaemonProfile().logger.warn("plugin-manager",`Could not refresh ${packageName} to @latest (${message}); using the already-installed copy`),this.pluginRoots=void 0;let plugin2=await this.importPlugin(packageName),entry2={packageName,version:plugin2.version,pluginName:plugin2.name,installedAt:new Date().toISOString()};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry2),this.saveRegistry(),entry2}catch{}throw Error(`Failed to install ${packageName}: ${message}`,{cause:err})}let plugin=await this.importPlugin(packageName);if(refreshLatest&&concreteLatest&&plugin.version!==concreteLatest)getDaemonProfile().logger.warn("plugin-manager",`${packageName} refreshed to ${plugin.version} but registry latest is ${concreteLatest} \u2014 a stale resolver/cache may still be in play`);if(this.corePlugins.has(plugin.name))getDaemonProfile().logger.info("plugin-manager",`External plugin '${packageName}' overrides core plugin '${plugin.name}'`);let entry={packageName,version:plugin.version,pluginName:plugin.name,installedAt:new Date().toISOString()};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry),this.saveRegistry(),getDaemonProfile().logger.info("plugin-manager",`Installed ${packageName}@${plugin.version} (${plugin.name})`),entry}async ensureDefaultPlugins(onStatus,skipPlugins=[],selectedAbilities){let installed=[],skipSet=new Set(skipPlugins),entries=selectedAbilities?getDefaultPluginsForAbilities(process.platform,selectedAbilities):getDefaultPluginsForPlatform(process.platform);for(let entry of entries){if(skipSet.has(entry.packageName)||skipSet.has(entry.pluginName)){getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} skipped by --skip-plugin`);continue}if(entry.isCore){getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} is bundled with the agent \u2014 skipping auto-install`);continue}if(this.registry.some((r)=>r.packageName===entry.packageName)){getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} already installed, skipping auto-install`);continue}onStatus?.(`Installing ${entry.label} (${entry.packageName})...`);try{await this.install(entry.packageName),installed.push(entry),onStatus?.(`Installed ${entry.label} (${entry.packageName})`)}catch(err){let message=err instanceof Error?err.message:String(err);getDaemonProfile().logger.error("plugin-manager",`Failed to auto-install ${entry.packageName}: ${message}`),onStatus?.(`Failed to install ${entry.label}: ${message}`)}}if(installed.length>0)this.loadRegistry();return installed}async installAndLoad(packageName,app,hostServices){let entry=await this.install(packageName),plugin=this.loaded.get(entry.packageName);if(plugin){this.registerPluginProviders(plugin,hostServices);let wrapped=this.wrapHostServices(plugin,hostServices);if(plugin.onServerStart)await plugin.onServerStart(app,wrapped);if(plugin.onServerReady)await plugin.onServerReady(app,wrapped)}return entry}async update(packageName){packageName=validatePluginPackageName(packageName),getDaemonProfile().logger.info("plugin-manager",`Updating ${packageName}...`);let existing=this.registry.find((e)=>e.packageName===packageName);if(!existing)throw Error(`Plugin ${packageName} is not installed. Use 'install' first.`);let registry=getPluginRegistry();try{if(Bun.spawnSync(["bun","install","-g",`${packageName}@latest`,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode!==0){let npmResult=Bun.spawnSync(["npm","install","-g",`${packageName}@latest`,"--registry",registry],{timeout:120000,stdout:"pipe",stderr:"pipe"});if(npmResult.exitCode!==0)throw Error(npmResult.stderr.toString().trim())}}catch(err){let message=err instanceof Error?err.message:String(err);throw Error(`Failed to update ${packageName}: ${message}`,{cause:err})}this.loaded.delete(packageName);let plugin=await this.importPlugin(packageName),entry={packageName,version:plugin.version,pluginName:plugin.name,installedAt:existing.installedAt};return this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.registry.push(entry),this.saveRegistry(),getDaemonProfile().logger.info("plugin-manager",`Updated ${packageName} to v${plugin.version}`),entry}async updateAll(){let results=[];for(let entry of[...this.registry])try{let updated=await this.update(entry.packageName);results.push({packageName:entry.packageName,success:!0,version:updated.version})}catch(err){results.push({packageName:entry.packageName,success:!1,error:err instanceof Error?err.message:String(err)})}return results}async removeAll(hostServices){let results=[],toRemove=[...this.registry];for(let entry of toRemove)try{await this.remove(entry.packageName,hostServices),results.push({packageName:entry.packageName,success:!0})}catch(err){results.push({packageName:entry.packageName,success:!1,error:err instanceof Error?err.message:String(err)})}return results}async remove(packageName,hostServices){packageName=validatePluginPackageName(packageName),getDaemonProfile().logger.info("plugin-manager",`Removing ${packageName}...`);let plugin=this.loaded.get(packageName);if(plugin?.onServerStop)try{await plugin.onServerStop()}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Error stopping ${packageName}: ${err}`)}if(plugin)hostServices?.serviceRegistry.unregisterPlugin(plugin.name),this.loaded.delete(packageName);let registryEntry=this.registry.find((e)=>e.packageName===packageName);if(!plugin&®istryEntry)hostServices?.serviceRegistry.unregisterPlugin(registryEntry.pluginName);let localDir=join(getVibecontrolsDir(),"agent-plugins");try{if(existsSync(join(localDir,"package.json")))Bun.spawnSync(["bun","remove",packageName],{cwd:localDir,timeout:60000,stdout:"pipe",stderr:"pipe"})}catch{}try{Bun.spawnSync(["bun","remove","-g",packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"})}catch{try{Bun.spawnSync(["npm","uninstall","-g",packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"})}catch{}}this.pluginRoots=void 0,this.registry=this.registry.filter((e)=>e.packageName!==packageName),this.saveRegistry(),getDaemonProfile().logger.info("plugin-manager",`Removed ${packageName}`)}async loadAll(){this.loaded.clear();let stale=[];for(let entry of this.registry)try{validatePluginPackageName(entry.packageName),await this.importPlugin(entry.packageName),getDaemonProfile().logger.info("plugin-manager",`Loaded ${entry.packageName} (${entry.pluginName})`)}catch(err){let code=err.code;if(code==="ENOENT_PLUGIN")getDaemonProfile().logger.info("plugin-manager",`${entry.packageName} not present on disk \u2014 dropping from registry`),stale.push(entry.packageName);else if(code==="EPLUGIN_LEGACY_SINGLETON")getDaemonProfile().logger.warn("plugin-manager",`Skipping ${entry.packageName}: ${err instanceof Error?err.message:String(err)}`);else getDaemonProfile().logger.warn("plugin-manager",`Failed to load ${entry.packageName} \u2014 dropping from registry: ${err}`),stale.push(entry.packageName)}if(stale.length>0){this.registry=this.registry.filter((e)=>!stale.includes(e.packageName));try{this.saveRegistry()}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to persist pruned registry: ${err}`)}}}async importPlugin(packageName){packageName=validatePluginPackageName(packageName);let mod={},packageRoot;{let roots=this.getGlobalRoots(),loaded=!1,attempts=[],allMissing=!0;for(let root of roots){let absolutePath=join(root,packageName);if(!existsSync(absolutePath)){attempts.push({path:absolutePath,error:"directory missing"});continue}allMissing=!1;try{packageRoot=absolutePath,mod=await import(absolutePath),loaded=!0;break}catch(err){attempts.push({path:absolutePath,error:err instanceof Error?err.message:String(err)})}try{let pkgJsonPath=join(absolutePath,"package.json");if(!existsSync(pkgJsonPath)){attempts.push({path:pkgJsonPath,error:"package.json missing"});continue}let pkgJson=JSON.parse(readFileSync(pkgJsonPath,"utf8")),entry=pkgJson.module??pkgJson.main??"./index.js",filePath=join(absolutePath,entry);packageRoot=absolutePath,mod=await import(filePath),loaded=!0;break}catch(err){attempts.push({path:absolutePath,error:err instanceof Error?err.message:String(err)})}}if(!loaded)try{packageRoot=this.resolvePackageRoot(packageName)??packageRoot,mod=await import(packageName),loaded=!0}catch(err){attempts.push({path:`bare:${packageName}`,error:err instanceof Error?err.message:String(err)})}if(!loaded){let detail=attempts.map((a)=>`${a.path} (${a.error})`).join("; "),err=Error(`Cannot find module '${packageName}': explicit root attempts and bare import failed: ${detail||"(no roots probed)"}`);if(allMissing&&roots.length>0)err.code="ENOENT_PLUGIN";throw err}}let plugin=this.extractPlugin(mod,`external:${packageName}`,getDaemonProfile());if(!plugin){if(this.hasLegacySingleton(mod)){let err=Error(`${packageName} uses removed singleton contract; upgrade to createPlugin(ctx) factory (set VIBECONTROLS_PLUGIN_CONTRACT_V2_ENFORCE=1 to refuse strictly)`);throw err.code="EPLUGIN_LEGACY_SINGLETON",err}throw Error(`${packageName} does not export a valid createPlugin(ctx) factory`)}if(this.validatePluginExport(plugin,packageName,packageRoot),!this.acceptCapabilities(plugin,`external:${packageName}`))throw Error(`${packageName} refused to load: missing required capabilities declaration`);if(packageRoot)Object.defineProperty(plugin,PLUGIN_PACKAGE_ROOT,{value:packageRoot,enumerable:!1,configurable:!1});if(this.loaded.set(packageName,plugin),this.shouldIsolate(plugin))try{let ctx=getDaemonProfile(),host=new PluginWorkerHost({modulePath:packageName,pluginName:plugin.name,ctx:{name:ctx.name,dataDir:ctx.dataDir}});await host.start(),this.workerHosts.set(plugin.name,host),ctx.logger.info("plugin-manager",`Isolated plugin '${plugin.name}' running in worker_threads`)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to isolate plugin '${plugin.name}'; falling back to in-host: ${err instanceof Error?err.message:String(err)}`)}return plugin}resolvePackageRoot(packageName){let meta=import.meta;if(typeof meta.resolve!=="function")return;try{let resolved=meta.resolve(packageName);if(!resolved.startsWith("file:"))return;let current=dirname(fileURLToPath2(resolved));while(current&¤t!==dirname(current)){if(existsSync(join(current,"package.json")))return current;current=dirname(current)}}catch{return}return}validatePluginExport(plugin,packageName,packageRoot){if(!PLUGIN_NAME_RE.test(plugin.name))throw Error(`${packageName} exports an invalid plugin name`);if(!plugin.version||plugin.version.length>64)throw Error(`${packageName} exports an invalid plugin version`);if(plugin.description&&plugin.description.length>500)throw Error(`${packageName} description is too long`);if(plugin.cliCommand&&!PLUGIN_NAME_RE.test(plugin.cliCommand))throw Error(`${packageName} exports an invalid CLI command`);if(plugin.apiPrefix){if(!PLUGIN_API_PREFIX_RE.test(plugin.apiPrefix)||plugin.apiPrefix.includes("..")||plugin.apiPrefix.includes("//"))throw Error(`${packageName} exports an invalid API prefix`)}if(plugin.publicPaths){if(plugin.publicPaths.length>20)throw Error(`${packageName} exports too many public paths`);for(let publicPath of plugin.publicPaths)if(!PLUGIN_PUBLIC_PATH_RE.test(publicPath)||publicPath.includes("..")||publicPath.includes("//"))throw Error(`${packageName} exports an invalid public path`)}if(plugin.dependencies&&plugin.dependencies.length>20)throw Error(`${packageName} exports too many dependencies`);if(plugin.tags){let allowedTags=new Set(["backend","frontend","cli","provider","adapter","integration"]);if(plugin.tags.length>allowedTags.size)throw Error(`${packageName} exports too many tags`);for(let tag of plugin.tags)if(!allowedTags.has(tag))throw Error(`${packageName} exports an invalid tag`)}if(plugin.ui){if(plugin.ui.title&&plugin.ui.title.length>120)throw Error(`${packageName} UI title is too long`);if(plugin.ui.staticDir){if(!packageRoot)throw Error(`${packageName} UI staticDir requires a package root`);let rootReal=realpathSync(packageRoot),staticReal=realpathSync(plugin.ui.staticDir);if(!statSync(staticReal).isDirectory()||!isPathInside(rootReal,staticReal))throw Error(`${packageName} UI staticDir must be inside the plugin package`)}}}pluginRoots;getPluginRoots(){if(this.pluginRoots!==void 0)return this.pluginRoots;let roots=[],seen=new Set,push=(p)=>{if(!p)return;if(seen.has(p))return;if(!existsSync(p))return;seen.add(p),roots.push(p)};try{let localDir=join(getVibecontrolsDir(),"agent-plugins","node_modules");push(localDir)}catch{}let extra=process.env.VIBECONTROLS_PLUGIN_ROOTS;if(extra){let rootsDelimiter=process.platform==="win32"?";":":";for(let p of extra.split(rootsDelimiter).map((s)=>s.trim()).filter(Boolean))push(p)}try{let dir=process.cwd(),root=dirname(dir)===dir?dir:"/";while(dir&&dir!==root&&dir!==dirname(dir))push(join(dir,"node_modules")),dir=dirname(dir)}catch{}try{let dir=dirname(fileURLToPath2(import.meta.url)),stop=dirname(dir)===dir?dir:"/";while(dir&&dir!==stop&&dir!==dirname(dir)){if(existsSync(join(dir,"package.json"))){push(join(dir,"node_modules"));break}dir=dirname(dir)}}catch{}try{push(join(osHomedir(),".bun","install","global","node_modules"))}catch{}try{let result=Bun.spawnSync(["npm","root","-g"],{stdout:"pipe",stderr:"pipe",timeout:1e4});if(result.exitCode===0)push(result.stdout.toString().trim())}catch{}return this.pluginRoots=roots,roots}getGlobalRoots(){return this.getPluginRoots()}getNpmGlobalRoot(){let roots=this.getPluginRoots();return roots.length>0?roots[0]:null}extractPlugin(mod,label,ctx){let factory=this.findFactory(mod);if(factory){let plugin=factory(ctx);if(!this.isVibePlugin(plugin))throw Error(`plugin '${label}' createPlugin(ctx) factory returned an invalid VibePlugin`);return plugin}if(this.hasLegacySingleton(mod)){let message=`plugin '${label}' uses removed singleton contract; upgrade to createPlugin(ctx) factory`;if(pluginContractV2EnforceMode())throw Error(message);getDaemonProfile().logger.warn("plugin-manager",message);return}return}findFactory(mod){if(typeof mod.createPlugin==="function")return mod.createPlugin;let defaultExport=mod.default;if(defaultExport&&typeof defaultExport.createPlugin==="function")return defaultExport.createPlugin;return}hasLegacySingleton(mod){if(this.isVibePlugin(mod.vibePlugin))return!0;let defaultExport=mod.default;if(defaultExport&&this.isVibePlugin(defaultExport.vibePlugin))return!0;if(this.isVibePlugin(defaultExport))return!0;return!1}isVibePlugin(value){if(!value||typeof value!=="object")return!1;let candidate=value;return typeof candidate.name==="string"&&typeof candidate.version==="string"}registerProviders(plugin,hostServices){this.registerPluginProviders(plugin,hostServices)}registerPluginProviders(plugin,hostServices){let isDefault=getCatalogEntry2(plugin.name)?.isDefault===!0,pinDefault=(type)=>{if(!isDefault)return;hostServices.serviceRegistry.setProviderDefault(type,plugin.name)};if(plugin.providers?.tunnel){if(hostServices.serviceRegistry.registerProvider("tunnel",plugin.providers.tunnel,plugin.name),pinDefault("tunnel"),plugin.tunnelDomainSuffixes)for(let suffix of plugin.tunnelDomainSuffixes)registerAllowedTunnelSuffix(suffix)}if(plugin.providers?.session)hostServices.serviceRegistry.registerProvider("session",plugin.providers.session,plugin.name),pinDefault("session");if(plugin.providers?.ai)hostServices.serviceRegistry.registerProvider("ai",plugin.providers.ai,plugin.name),pinDefault("ai")}async dispatchCliSetup(program,hostServices){for(let plugin of this.getAllPlugins())if(plugin.onCliSetup)try{await plugin.onCliSetup(program,this.wrapHostServices(plugin,hostServices))}catch(err){getDaemonProfile().logger.warn("plugin-manager",`onCliSetup failed for ${plugin.name}: ${err}`)}}async dispatchServerStart(app,hostServices){let sorted=this.sortAllByDependencies(),failures=[];for(let plugin of sorted){if(this.registerPluginProviders(plugin,hostServices),plugin.onServerStart){let startedAt=Date.now();try{await plugin.onServerStart(app,this.wrapHostServices(plugin,hostServices)),(async()=>{try{let{telemetryService}=await import("./telemetry-rew0mtj2.js");telemetryService.emit("plugin.activated",{plugin_name:plugin.name,version:plugin.version,duration_ms:Date.now()-startedAt})}catch{}})()}catch(err){let message=err instanceof Error?err.message:String(err);getDaemonProfile().logger.error("plugin-manager",`onServerStart failed for ${plugin.name}: ${message}`),(async()=>{try{let{telemetryService}=await import("./telemetry-rew0mtj2.js");telemetryService.emit("plugin.failed",{plugin_name:plugin.name,error_class:err instanceof Error?err.constructor.name:"Error"})}catch{}})();let entry=this.registry.find((e)=>e.pluginName===plugin.name);failures.push({plugin:plugin.name,packageName:entry?.packageName,error:message})}}this.registerPluginProviders(plugin,hostServices)}return{failures}}async dispatchServerReady(app,hostServices){for(let plugin of this.getAllPlugins())if(plugin.onServerReady)try{await plugin.onServerReady(app,this.wrapHostServices(plugin,hostServices))}catch(err){getDaemonProfile().logger.warn("plugin-manager",`onServerReady failed for ${plugin.name}: ${err}`)}}async provisionDefaultPrereqs(app,hostServices){let{runPluginPrereqs}=await import("./prereqs-runner-k85knvak.js"),skipInstall=process.env.VIBE_SKIP_PREREQ_INSTALL==="1",platform=process.platform,newlyInstalled=[],metaPkgs=getDefaultPluginsForPlatform(process.platform).map((p)=>p.packageName);if(!skipInstall)for(let pkg of metaPkgs){if(this.loaded.has(pkg))continue;try{getDaemonProfile().logger.info("plugin-manager",`Installing default meta ${pkg}`),await this.install(pkg,{refreshLatest:!0}),newlyInstalled.push(pkg)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to install default meta ${pkg}: ${err}`)}}let providerPkgs=new Set;for(let[metaPkg,metaPlugin]of this.getAllLoaded()){let mps=metaPlugin.metaProviders??[];if(mps.length>0)getDaemonProfile().logger.info("plugin-manager",`Meta ${metaPkg} declares ${mps.length} provider(s) (platform=${platform})`);for(let prov of mps)if(prov.defaultOn?.includes(platform))providerPkgs.add(prov.packageName)}if(getDaemonProfile().logger.info("plugin-manager",`Default providers for ${platform}: ${[...providerPkgs].join(", ")||"(none)"}`),!skipInstall)for(let pkg of providerPkgs){if(this.loaded.has(pkg))continue;try{getDaemonProfile().logger.info("plugin-manager",`Installing default provider ${pkg} (declared by a meta plugin)`),await this.install(pkg,{refreshLatest:!0}),newlyInstalled.push(pkg)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to install default provider ${pkg}: ${err}`)}}let defaults=new Set([...metaPkgs,...providerPkgs]);for(let[packageName,plugin]of this.getAllLoaded()){if(!defaults.has(packageName))continue;try{let r=await runPluginPrereqs(plugin,packageName,app,this.db??null,{skipInstall});if(!r.noProtocol&&!r.satisfied){let pending=r.pendingSudo.map((p)=>p.name).join(", ")||"none";getDaemonProfile().logger.warn("plugin-manager",`${packageName}: prerequisites still unsatisfied after install (pendingSudo: ${pending})`)}}catch(err){getDaemonProfile().logger.warn("plugin-manager",`${packageName}: prereq provisioning failed: ${err}`)}}for(let pkg of newlyInstalled){let plugin=this.loaded.get(pkg);if(!plugin)continue;try{this.registerPluginProviders(plugin,hostServices);let wrapped=this.wrapHostServices(plugin,hostServices);if(plugin.onServerStart)await plugin.onServerStart(app,wrapped);if(plugin.onServerReady)await plugin.onServerReady(app,wrapped)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`${pkg}: provider start failed: ${err}`)}}await this.electPlatformDefaultProviders(hostServices,platform)}inferProviderTypeForPlugin(hostServices,pluginName){for(let type of PROVIDER_TYPES)if(hostServices.serviceRegistry.listProvidersForType(type).some((e)=>e.pluginName===pluginName))return type;return}async electPlatformDefaultProviders(hostServices,platform){for(let[,metaPlugin]of this.getAllLoaded())for(let prov of metaPlugin.metaProviders??[]){if(!prov.pluginName)continue;if(!prov.defaultOn?.includes(platform))continue;let type=prov.providerType??this.inferProviderTypeForPlugin(hostServices,prov.pluginName);if(!type)continue;if(!hostServices.serviceRegistry.listProvidersForType(type).some((e)=>e.pluginName===prov.pluginName))continue;try{let existing=await this.getDefaultProvider(type);if(existing&&existing!==prov.pluginName)continue;if(hostServices.serviceRegistry.setProviderDefault(type,prov.pluginName),!existing)await this.setDefaultProvider(type,prov.pluginName).catch(()=>{});getDaemonProfile().logger.info("plugin-manager",`Elected platform-default '${type}' provider '${prov.pluginName}' for ${platform}`)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`Failed to elect default '${type}' provider '${prov.pluginName}': ${err}`)}}}async dispatchServerStop(context){for(let plugin of this.getAllPlugins())if(plugin.onServerStop)try{await plugin.onServerStop(context)}catch(err){getDaemonProfile().logger.warn("plugin-manager",`onServerStop failed for ${plugin.name}: ${err}`)}}async runPluginNuke(packageName,hostServices,ctx){let targets=packageName?this.loaded.has(packageName)?[[packageName,this.loaded.get(packageName)]]:[]:Array.from(this.loaded.entries()),results=[];for(let[pkg,plugin]of targets){if(!plugin.onNuke)continue;try{let wrapped=this.wrapHostServices(plugin,hostServices),out=await plugin.onNuke(wrapped,ctx);results.push({plugin:pkg,ok:!0,reaped:out?.reaped??[],notes:out?.notes??[]}),getDaemonProfile().logger.info("plugin-manager",`onNuke ${ctx.dryRun?"(dry-run) ":""}completed for ${pkg}`,{reaped:out?.reaped??[]})}catch(err){let msg=err instanceof Error?err.message:String(err);results.push({plugin:pkg,ok:!1,reaped:[],notes:[],error:msg}),getDaemonProfile().logger.warn("plugin-manager",`onNuke failed for ${pkg}: ${msg}`)}}return{results}}async reloadAll(app,hostServices){await this.dispatchServerStop({reason:"reload"});for(let plugin of this.loaded.values())hostServices.serviceRegistry.unregisterPlugin(plugin.name);this.loaded.clear(),this.corePlugins.clear(),this.loadRegistry(),await this.loadCorePlugins(),await this.loadAll(),await this.dispatchServerStart(app,hostServices),await this.dispatchServerReady(app,hostServices)}sortAllByDependencies(){let allPlugins=this.getAllPlugins(),pluginMap=new Map;for(let plugin of allPlugins)pluginMap.set(plugin.name,plugin);let visited=new Set,visiting=new Set,sorted=[],visit=(name)=>{if(visited.has(name))return;if(visiting.has(name))throw Error(`Plugin dependency cycle detected at '${name}'`);let plugin=pluginMap.get(name);if(!plugin)return;if(visiting.add(name),plugin.dependencies)for(let dep of plugin.dependencies){if(!pluginMap.has(dep)){getDaemonProfile().logger.warn("plugin-manager",`Plugin '${plugin.name}' depends on missing plugin '${dep}'`);continue}visit(dep)}visiting.delete(name),visited.add(name),sorted.push(plugin)};for(let plugin of allPlugins)visit(plugin.name);return sorted}sortByDependencies(){let entries=Array.from(this.loaded.entries()),nameToPackage=new Map;for(let[pkg,plugin]of entries)nameToPackage.set(plugin.name,pkg);let visited=new Set,sorted=[],visit=(pkg)=>{if(visited.has(pkg))return;visited.add(pkg);let plugin=this.loaded.get(pkg);if(plugin?.dependencies)for(let dep of plugin.dependencies){let depPkg=nameToPackage.get(dep);if(depPkg&&this.loaded.has(depPkg))visit(depPkg)}let loadedPlugin=this.loaded.get(pkg);if(loadedPlugin)sorted.push([pkg,loadedPlugin])};for(let[pkg]of entries)visit(pkg);return sorted}getPluginByKey(key){for(let[,plugin]of this.loaded)if(plugin.name===key)return plugin;return this.corePlugins.get(key)}getAllPlugins(){let merged=new Map;for(let[key,plugin]of this.corePlugins)merged.set(key,plugin);for(let[,plugin]of this.loaded)merged.set(plugin.name,plugin);return Array.from(merged.values())}getAllPluginsByTag(tag){return this.getAllPlugins().filter((plugin)=>plugin.tags?.includes(tag)??!1)}resolvePluginChain(tokens){let allKeys=new Set;for(let p of this.getAllPlugins())allKeys.add(p.name);let chain=[],commandIdx=-1;for(let i=0;i<tokens.length;i++){if(tokens[i].startsWith("-")){commandIdx=i;break}if(allKeys.has(tokens[i]))chain.push(tokens[i]);else{commandIdx=i;break}}if(commandIdx===-1)return{chain,command:"",args:[]};return{chain,command:tokens[commandIdx],args:tokens.slice(commandIdx+1)}}async getDefaultProvider(type){if(!this.db)return null;return await this.db.getConfig(`${PROVIDER_DEFAULT_PREFIX}${type}`)??null}async setDefaultProvider(type,pluginName){if(!this.db)throw Error("Cannot set default provider: PluginManager was constructed without a database reference");if(!this.getPluginByKey(pluginName))throw Error(`Plugin '${pluginName}' not found`);await this.db.setConfig(`${PROVIDER_DEFAULT_PREFIX}${type}`,pluginName),getDaemonProfile().logger.info("plugin-manager",`Set default '${type}' provider to '${pluginName}'`)}getPluginDetails(){let coreInfos=this.getCorePluginDetails(),externalInfos=this.getExternalPluginDetails(),merged=new Map;for(let info of coreInfos)merged.set(info.pluginName,info);for(let info of externalInfos)merged.set(info.pluginName,info);return Array.from(merged.values())}getCorePluginDetails(){let infos=[];for(let[name,plugin]of this.corePlugins)infos.push({packageName:`@vibecontrols/plugin-${name}`,pluginName:name,version:plugin.version,description:plugin.description,tags:plugin.tags,cliCommand:plugin.cliCommand,apiPrefix:plugin.apiPrefix,dependencies:plugin.dependencies,installedAt:"",loaded:!0,isCore:!0,hasUI:!!plugin.ui,uiUrl:plugin.ui?`/ui/${name}`:void 0,hasCliSetup:!!plugin.onCliSetup,hasServerStart:!!plugin.onServerStart,hasServerStop:!!plugin.onServerStop,hasServerReady:!!plugin.onServerReady,hasProviders:!!(plugin.providers?.tunnel||plugin.providers?.session||plugin.providers?.ai)});return infos}getExternalPluginDetails(){return this.registry.map((entry)=>{let plugin=this.loaded.get(entry.packageName),catalogEntry=getCatalogEntry2(entry.pluginName);return{packageName:entry.packageName,pluginName:entry.pluginName,version:plugin?.version??entry.version,description:plugin?.description,tags:plugin?.tags,cliCommand:plugin?.cliCommand,apiPrefix:plugin?.apiPrefix,dependencies:plugin?.dependencies,installedAt:entry.installedAt,loaded:!!plugin,isCore:catalogEntry?.isCore===!0,hasUI:!!plugin?.ui,uiUrl:plugin?.ui?`/ui/${entry.pluginName}`:void 0,hasCliSetup:!!plugin?.onCliSetup,hasServerStart:!!plugin?.onServerStart,hasServerStop:!!plugin?.onServerStop,hasServerReady:!!plugin?.onServerReady,hasProviders:!!(plugin?.providers?.tunnel||plugin?.providers?.session||plugin?.providers?.ai)}})}getRegistry(){return[...this.registry]}getLoaded(packageName){return this.loaded.get(packageName)}getAllLoaded(){return new Map(this.loaded)}isInstalled(packageName){return this.registry.some((e)=>e.packageName===packageName)}isLoaded(packageName){return this.loaded.has(packageName)}}
|
|
5
5
|
export{ServiceRegistry,PLUGIN_CATALOG,ABILITIES,CORE_PLUGIN_NAMES,getDefaultPluginsForAbilities,TRUSTED_PACKAGES,isCriticalPlugin,AVAILABLE_PLUGINS,getCatalogEntry,collectPluginBinaries,isTrustedPackage,resolvePluginByShortName,getPluginPackageRoot,validatePluginPackageName,DEFAULT_PLUGINS2 as DEFAULT_PLUGINS,PluginManager};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{listTrackedByOwner,spawnTracked}from"./index-926vt87h.js";import{LifecycleManager,createAgentDatabase,fetchEncryptionKey}from"./index-
|
|
2
|
+
import{listTrackedByOwner,spawnTracked}from"./index-926vt87h.js";import{LifecycleManager,createAgentDatabase,fetchEncryptionKey}from"./index-g9h64dn9.js";import{resolveSafePath}from"./index-skmkfyzb.js";import{readTunnelState,startBootstrapTunnel,stopBootstrapTunnel}from"./index-95nmejfd.js";import{ServiceManager}from"./index-34syaqa8.js";import{redactUnknownSecrets}from"./index-y5q0m3cx.js";import{CliContributorRegistry,createIframeBridge,createUIServer,renderUiBootstrapHtml}from"./index-n3y918j2.js";import{PluginManager,ServiceRegistry,isCriticalPlugin}from"./index-h6a1kkfk.js";import{getOsAdapter}from"./index-srbb2214.js";import{broadcast,createEventsWs,createLogWs,createPluginRouter,createTerminalProxy,dispatchProfileRequest,mountProfileRoutes,rateLimitPlugin}from"./index-brtw3j8x.js";import{createAuthPlugin,getAgentApiKey,getApiKeyFromHeaders,getUiCookieFromRequest,isValidAgentApiKey,isValidUiPluginName,issueIframeToken,issueScopedIframeToken,issueTerminalCookieToken,issueUiCookieToken,markPluginPublicPathsDirty,verifyIframeToken,verifyIframeTokenWithReason,verifyUiCookieToken}from"./index-b4wy3jrt.js";import{Elysia,t}from"./index-rnk0kny8.js";import{getAgentConfigSeed,readAgentConfig,writeAgentConfig,writeAgentConfigAwaitSecrets}from"./index-2pqv0bya.js";import{assertAllowedGatewayUrl,currentProfileOrNull,enterProfileContext,enterRequestContext,extractRequestId,gatewayClient,generateRequestId,getDaemonProfile,getOrCreateProfile,getRequestId,profileRegistry}from"./index-7pdmqbj8.js";import{getDefaultProfile,getInstances,getPluginRegistry,getProfileDataDir,getVibecontrolsDir,getVibecontrolsProductDir,getVibecontrolsProfile,setDefaultProfile,validateAgentName}from"./index-bysm7taq.js";import{__require,__toESM}from"./index-e9rt4m94.js";var isBun=typeof new Headers()?.toJSON==="function",processHeaders=(headers)=>{if(isBun)return Object.keys(headers.toJSON()).join(", ");let keys="",i=0;return headers.forEach((_,key)=>{if(i)keys=keys+", "+key;else keys=key;i++}),keys},cors=(config)=>{let{aot=!0,origin=!0,methods=!0,allowedHeaders=!0,exposeHeaders=!0,credentials=!0,maxAge=5,preflight=!0}=config??{};if(Array.isArray(allowedHeaders))allowedHeaders=allowedHeaders.join(", ");if(Array.isArray(exposeHeaders))exposeHeaders=exposeHeaders.join(", ");let origins=typeof origin==="boolean"?void 0:Array.isArray(origin)?origin:[origin],app=new Elysia({name:"@elysiajs/cors",seed:config,aot}),anyOrigin=origins?.some((o)=>o==="*"),originMap={};if(origins){for(let origin2 of origins)if(typeof origin2==="string")originMap[origin2]=!0}let processOrigin=(origin2,request,from)=>{if(Array.isArray(origin2))return origin2.some((o)=>processOrigin(o,request,from));switch(typeof origin2){case"string":if(from in originMap)return!0;let fromProtocol=from.indexOf("://");if(fromProtocol!==-1)from=from.slice(fromProtocol+3);return origin2===from;case"function":return origin2(request)===!0;case"object":if(origin2 instanceof RegExp)return origin2.test(from)}return!1},handleOrigin=(set,request)=>{if(origin===!0){set.headers.vary="*",set.headers["access-control-allow-origin"]=request.headers.get("Origin")||"*";return}if(anyOrigin){set.headers.vary="*",set.headers["access-control-allow-origin"]="*";return}if(!origins?.length)return;if(origins.length){let from=request.headers.get("Origin")??"";for(let i=0;i<origins.length;i++)if(processOrigin(origins[i],request,from)===!0){set.headers.vary=origin?"Origin":"*",set.headers["access-control-allow-origin"]=from||"*";return}}set.headers.vary="Origin"},handleMethod=(set,method)=>{if(!method)return;if(methods===!0)return set.headers["access-control-allow-methods"]=method??"*";if(methods===!1||!methods?.length)return;if(methods==="*")return set.headers["access-control-allow-methods"]="*";if(!Array.isArray(methods))return set.headers["access-control-allow-methods"]=methods;set.headers["access-control-allow-methods"]=methods.join(", ")},defaultHeaders={};if(typeof exposeHeaders==="string")defaultHeaders["access-control-expose-headers"]=exposeHeaders;if(typeof allowedHeaders==="string")defaultHeaders["access-control-allow-headers"]=allowedHeaders;if(credentials===!0)defaultHeaders["access-control-allow-credentials"]="true";app.headers(defaultHeaders);function handleOption({set,request,headers}){if(handleOrigin(set,request),handleMethod(set,request.headers.get("access-control-request-method")),allowedHeaders===!0||exposeHeaders===!0){if(allowedHeaders===!0)set.headers["access-control-allow-headers"]=headers["access-control-request-headers"];if(exposeHeaders===!0)set.headers["access-control-expose-headers"]=Object.keys(headers).join(",")}if(maxAge)set.headers["access-control-max-age"]=maxAge.toString();return new Response(null,{status:204})}if(preflight)app.options("/",handleOption).options("/*",handleOption);return app.onRequest(function({set,request}){if(handleOrigin(set,request),preflight&&request.method==="OPTIONS")return handleOption({set,request,headers:isBun?request.headers.toJSON():Object.fromEntries(request.headers.entries())});if(handleMethod(set,request.method),allowedHeaders===!0||exposeHeaders===!0){let headers=processHeaders(request.headers);if(allowedHeaders===!0)set.headers["access-control-allow-headers"]=headers;if(exposeHeaders===!0)set.headers["access-control-expose-headers"]=headers}})};import{createHash}from"crypto";var NS="idempotency",TTL_MS=300000,MAX_ENTRIES=1e4;function cacheKey(method,path,rawKey,bodyHash){return createHash("sha256").update(`${method}|${path}|${rawKey}|${bodyHash}`).digest("hex")}async function readCached(db,key){let raw=await db.getPluginState(NS,key);if(!raw)return null;try{let entry=JSON.parse(raw);if(entry.expiresAt<Date.now())return await db.deletePluginState(NS,key),null;return entry}catch{return null}}async function writeCached(db,key,entry){await db.setPluginState(NS,key,JSON.stringify(entry))}async function sweepIdempotency(db){let all=await db.getAllPluginState(NS),now=Date.now(),removed=0,live=[];for(let e of all)try{if(JSON.parse(e.value).expiresAt<now)await db.deletePluginState(NS,e.key),removed+=1;else live.push({key:e.key,updatedAt:e.updatedAt})}catch{await db.deletePluginState(NS,e.key),removed+=1}if(live.length>MAX_ENTRIES){live.sort((a,b)=>(a.updatedAt??"").localeCompare(b.updatedAt??""));let overage=live.slice(0,live.length-MAX_ENTRIES);for(let entry of overage)await db.deletePluginState(NS,entry.key),removed+=1}if(removed>0)getDaemonProfile().logger.info("idempotency",`Swept ${removed} expired/over-cap entries`)}var sweepTimer=null,SWEEP_INTERVAL_MS=600000;function startSweepIfNeeded(deps){if(sweepTimer)return;sweepTimer=setInterval(()=>{let db=deps.getDb();if(!db)return;sweepIdempotency(db).catch((err)=>{getDaemonProfile().logger.warn("idempotency","Sweep failed",{error:String(err)})})},SWEEP_INTERVAL_MS),sweepTimer.unref?.()}function idempotencyPlugin(deps){return startSweepIfNeeded(deps),new Elysia({name:"plugin/idempotency"}).onBeforeHandle(async({request,path,set,store})=>{if(request.method!=="POST"&&request.method!=="PUT")return;let rawKey=request.headers.get("x-idempotency-key")??request.headers.get("X-Idempotency-Key");if(!rawKey)return;if(rawKey.length<8||rawKey.length>200)return set.status=400,{error:"X-Idempotency-Key must be 8\u2013200 chars",code:"BAD_REQUEST"};let db=deps.getDb();if(!db)return;let bodyHash="no-body";try{let text=await request.clone().text();bodyHash=createHash("sha256").update(text).digest("hex")}catch{}let key=cacheKey(request.method,path,rawKey,bodyHash),cached=await readCached(db,key);if(cached){set.status=cached.status;let headers=set.headers??={};headers["Content-Type"]=cached.contentType,headers["X-Idempotency-Replay"]="true",store.__idemReplay=cached.body;try{return JSON.parse(cached.body)}catch{return cached.body}}store.__idemKey=key}).onAfterHandle(async({response,set,store})=>{let key=store.__idemKey;if(!key)return;let db=deps.getDb();if(!db)return;let status=set.status??200;if(status<200||status>=300)return;let body=typeof response==="string"?response:JSON.stringify(response),contentType=set.headers?.["Content-Type"]??"application/json";try{await writeCached(db,key,{status,body,contentType,expiresAt:Date.now()+TTL_MS})}catch(err){getDaemonProfile().logger.warn("idempotency","Failed to persist response",{error:String(err)})}}).as("global")}import os from"os";import{readFileSync as readFileSync2}from"fs";import{join as join2}from"path";var DISABLED_REGISTRY={enabled:!1,async scrape(){return`# vibecontrols agent metrics disabled (OTel SDK not initialised)
|
|
3
3
|
`},recordHttpRequest(){},setPluginsLoaded(){}},cached=null,initPromise=null;async function getMetricsRegistry(opts){if(cached)return cached;if(initPromise)return initPromise;return initPromise=(async()=>{try{let{MeterProvider}=await import("./index-7t06bbkg.js").then((m)=>__toESM(m.default,1)),{PrometheusExporter,PrometheusSerializer}=await import("./index-q7zah1sn.js").then((m)=>__toESM(m.default,1)),reader=new PrometheusExporter({preventServerStart:!0}),serializer=new PrometheusSerializer,meter=new MeterProvider({readers:[reader]}).getMeter("vibecontrols-agent"),infoGauge=meter.createGauge("vibecontrols_agent_info",{description:"Static info about the agent process; value is always 1."});meter.createObservableGauge("vibecontrols_agent_uptime_seconds",{description:"Process uptime in seconds."}).addCallback((res)=>{res.observe(process.uptime())});let httpCounter=meter.createCounter("vibecontrols_http_requests_total",{description:"Total HTTP requests handled by the agent, labelled by method, route, status."}),pluginsLoadedSnapshot={};meter.createObservableGauge("vibecontrols_plugins_loaded",{description:"Plugins loaded by the agent, broken down by status (loaded, failed, etc.)."}).addCallback((res)=>{for(let[status,count]of Object.entries(pluginsLoadedSnapshot))res.observe(count,{status})}),infoGauge.record(1,{version:opts?.version??"unknown",profile:opts?.profile??"default"});let handle={enabled:!0,async scrape(){let collected=await reader.collect();if(!collected.resourceMetrics)return"";return serializer.serialize(collected.resourceMetrics)},recordHttpRequest(method,route,status){httpCounter.add(1,{method,route,status:String(status)})},setPluginsLoaded(byStatus){pluginsLoadedSnapshot={...byStatus}}};return cached=handle,getDaemonProfile().logger.info("metrics","OTel Prometheus registry initialised"),handle}catch(err){return getDaemonProfile().logger.warn("metrics","Failed to initialise OTel metrics; /metrics will return a stub body",{error:err instanceof Error?err.message:String(err)}),cached=DISABLED_REGISTRY,DISABLED_REGISTRY}})(),initPromise}function recordHttpRequest(method,route,status){if(!cached)return;cached.recordHttpRequest(method,route,status)}function setPluginsLoaded(byStatus){if(!cached)return;cached.setPluginsLoaded(byStatus)}var LOOPBACK_ADDRS=new Set(["127.0.0.1","::1"]),warnedAnonEnv=!1;function maybeWarnAnonEnv(){if(warnedAnonEnv)return;if(process.env.METRICS_ANONYMOUS_LOCALHOST==="true")warnedAnonEnv=!0,getDaemonProfile().logger.warn("metrics","METRICS_ANONYMOUS_LOCALHOST=true \u2014 /metrics is open to TCP loopback peers without an API key. Prefer issuing a scoped API key for your scraper.")}function normalizeAddr(addr){let cleaned=addr.replace(/^\[|\]$/g,"");if(cleaned.startsWith("::ffff:"))return cleaned.slice(7);return cleaned}function isLoopbackPeer(request,server){let peer=server?.requestIP(request)?.address;if(!peer)return!1;return LOOPBACK_ADDRS.has(normalizeAddr(peer))}function createMetricsRoutes(){return maybeWarnAnonEnv(),new Elysia({name:"routes/metrics"}).get("/metrics",async({request,server,set})=>{if(!(process.env.METRICS_ANONYMOUS_LOCALHOST==="true"&&isLoopbackPeer(request,server))){let provided=getApiKeyFromHeaders(Object.fromEntries(request.headers.entries()));if(!isValidAgentApiKey(provided))return set.status=401,set.headers["content-type"]="application/json",JSON.stringify({error:"Unauthorized",message:"Invalid or missing API key"})}let body=await(await getMetricsRegistry()).scrape();return set.headers["content-type"]="text/plain; version=0.0.4; charset=utf-8",body})}import{randomBytes,timingSafeEqual}from"crypto";import{chmodSync,existsSync,mkdirSync,readFileSync,unlinkSync,writeFileSync}from"fs";import{dirname,join}from"path";var inMemoryToken=null;function pairingTokenPath(){return join(getVibecontrolsDir(),"pairing-token")}function generateToken(){return randomBytes(32).toString("base64url")}function constantTimeMatch(a,b){let aBuf=Buffer.from(a),bBuf=Buffer.from(b);if(aBuf.length!==bBuf.length)return!1;try{return timingSafeEqual(aBuf,bBuf)}catch{return!1}}function mintPairingToken(){if(inMemoryToken)return inMemoryToken;let path=pairingTokenPath();try{if(existsSync(path)){let existing=readFileSync(path,"utf8").trim();if(existing.length>=32)return inMemoryToken=existing,existing}}catch{}let token=generateToken();try{mkdirSync(dirname(path),{recursive:!0}),writeFileSync(path,token,{encoding:"utf8"});try{chmodSync(path,384)}catch{}}catch(err){}return inMemoryToken=token,token}function isValidPairingToken(candidate){if(!candidate)return!1;if(!inMemoryToken)return!1;return constantTimeMatch(candidate,inMemoryToken)}function invalidatePairingToken(){inMemoryToken=null;try{let path=pairingTokenPath();if(existsSync(path))unlinkSync(path)}catch{}}function getPairingTokenFromHeaders(headers){return headers["x-pairing-token"]??headers["X-Pairing-Token"]??null}function getMachineId(){let hostname=os.hostname(),platform=os.platform(),arch=os.arch(),hasher=new Bun.CryptoHasher("sha256");return hasher.update(`${hostname}-${platform}-${arch}`),hasher.digest("hex").substring(0,16)}function getAgentVersion(){for(let depth of["..","../.."])try{let pkgPath=join2(import.meta.dir,depth,"package.json"),v=JSON.parse(readFileSync2(pkgPath,"utf8")).version;if(v)return v}catch{}return"0.0.0"}async function getActiveTunnelUrl(serviceRegistry){let providers=[],defaultProvider=serviceRegistry.getProvider("tunnel");if(defaultProvider)providers.push(defaultProvider);for(let entry of serviceRegistry.listProvidersForType("tunnel")){let provider=serviceRegistry.getProviderByName("tunnel",entry.pluginName);if(provider&&!providers.includes(provider))providers.push(provider)}for(let provider of providers){if(!provider.getActiveTunnelUrl)continue;let url=await provider.getActiveTunnelUrl();if(url)return url}return null}function createPreConfigRoutes({serviceRegistry,runFinalize,getDb}){let version=getAgentVersion(),machineId=getMachineId();return new Elysia({prefix:"/api/agent"}).get("/identity",()=>({machineId,hostname:os.hostname(),platform:os.platform(),arch:os.arch(),version,uptime:process.uptime()})).get("/version",()=>({version,runtime:"bun",runtimeVersion:typeof Bun<"u"?Bun.version:"unknown"})).get("/api-key",({request,server,set})=>{if(!isLoopbackPeer(request,server)){let provided=getApiKeyFromHeaders(Object.fromEntries(request.headers.entries()));if(!isValidAgentApiKey(provided))return set.status=401,{error:"Unauthorized",message:"Invalid or missing API key"}}return{apiKey:getAgentApiKey()}}).post("/iframe-token",({body,request,set})=>{let provided=getApiKeyFromHeaders(Object.fromEntries(request.headers.entries()));if(!isValidAgentApiKey(provided))return set.status=401,{error:"Unauthorized",message:"Invalid or missing API key"};try{if(Array.isArray(body.pathPrefixes)&&body.pathPrefixes.length>0)return issueScopedIframeToken({pathPrefixes:body.pathPrefixes},body.ttlSeconds??void 0);if(!body.pathPrefix)return set.status=400,{error:"BadRequest",message:"Provide either pathPrefix (string) or pathPrefixes (string[])"};return issueIframeToken(body.pathPrefix,body.ttlSeconds??void 0)}catch(err){return set.status=400,{error:"BadRequest",message:err instanceof Error?err.message:String(err)}}},{body:t.Object({pathPrefix:t.Optional(t.String({minLength:1})),pathPrefixes:t.Optional(t.Array(t.String({minLength:1}),{minItems:1,maxItems:16})),ttlSeconds:t.Optional(t.Number())})}).post("/terminal-exchange",({body,set})=>{let{sid,token}=body;if(!sid||!token)return set.status=400,{error:"BadRequest",message:"sid and token required"};if(sid.includes("/")||sid.includes("\x00")||sid.includes(`
|
|
4
|
-
`)||sid.includes("|"))return set.status=400,{error:"BadRequest",message:"invalid sid"};let expectedPath=`/terminal/${sid}`,verifyResult=verifyIframeTokenWithReason(token,expectedPath);if(!verifyResult.ok)return getDaemonProfile().logger.warn("terminal-exchange","iframe token rejected",{sid,reason:verifyResult.reason}),set.status=401,{error:"Unauthorized",message:"invalid iframe token",code:verifyResult.reason==="expired"?"expired":verifyResult.reason==="replayed"?"replayed":"invalid"};let cookie=issueTerminalCookieToken(sid,600),cookieName=`vt_term_${sid}`;return set.headers["set-cookie"]=`${cookieName}=${cookie.token}; HttpOnly; Secure; SameSite=None; Path=/terminal/${sid}; Max-Age=600`,set.status=204,null},{body:t.Object({sid:t.String({minLength:1}),token:t.String({minLength:1})})}).post("/ui-exchange",({body,set})=>{let{plugin,token}=body;if(!plugin||!token)return set.status=400,{error:"BadRequest",message:"plugin and token required"};if(!isValidUiPluginName(plugin))return set.status=400,{error:"BadRequest",message:"invalid plugin name"};let expectedPath=`/ui/${plugin}`;if(!verifyIframeToken(token,expectedPath))return set.status=401,{error:"Unauthorized",message:"invalid iframe token"};let cookie=issueUiCookieToken(plugin,600),cookieName=`vt_ui_${plugin}`;return set.headers["set-cookie"]=`${cookieName}=${cookie.token}; HttpOnly; Secure; SameSite=None; Path=/ui/${plugin}/; Max-Age=600`,set.status=204,null},{body:t.Object({plugin:t.String({minLength:1}),token:t.String({minLength:1})})}).post("/ui-exchange-plugin",({body,set})=>{let{plugin,token}=body;if(!plugin||!token)return set.status=400,{error:"BadRequest",message:"plugin and token required"};if(!isValidUiPluginName(plugin))return set.status=400,{error:"BadRequest",message:"invalid plugin name"};if(![`/api/plugins/${plugin}/ui`,`/${plugin}`].find((p)=>verifyIframeToken(token,p)))return set.status=401,{error:"Unauthorized",message:"invalid iframe token"};let cookie=issueUiCookieToken(plugin,600),cookieName=`vt_ui_${plugin}`;return set.headers["set-cookie"]=`${cookieName}=${cookie.token}; HttpOnly; Secure; SameSite=None; Path=/api/plugins/${plugin}/ui/; Max-Age=600`,set.status=204,null},{body:t.Object({plugin:t.String({minLength:1}),token:t.String({minLength:1})})}).get("/system",()=>({hostname:os.hostname(),platform:os.platform(),arch:os.arch(),release:os.release(),cpus:os.cpus().length,totalMemory:os.totalmem(),freeMemory:os.freemem(),uptime:os.uptime(),bunVersion:typeof Bun<"u"?Bun.version:void 0,agentVersion:version,homeDir:os.homedir(),cwd:process.cwd(),environment:"production"})).get("/tunnel",async()=>{let url=await getActiveTunnelUrl(serviceRegistry);if(!url)return{tunnelUrl:null,message:"No tunnel provider registered"};return{tunnelUrl:url}}).get("/status",()=>{let providers=["tunnel","session","ai"].filter((type)=>serviceRegistry.hasProvider(type));return{state:getDaemonProfile().getBootState(),status:getDaemonProfile().getBootState(),version,providers,timestamp:new Date().toISOString()}}).get("/gateway-auth",()=>({configured:gatewayClient.isConfigured(),config:redactUnknownSecrets(gatewayClient.getConfig()),state:getDaemonProfile().getBootState()})).post("/retry-config",async({set})=>{let{getFinalizeRetryHandle}=await import("./finalize-retry-handle-registry-vbq5qhj1.js"),handle=getFinalizeRetryHandle();if(!handle)return set.status=409,{ok:!1,error:"No finalize retry worker is active. The agent either never had cached credentials to retry with, or it already reached the ready state."};let result=await handle.forceRetryNow();if(!result.ok)set.status=503;return{ok:result.ok,error:result.ok?void 0:result.error,state:getDaemonProfile().getBootState(),worker:handle.getState()}}).post("/gateway-auth",async({body,request,set})=>{let stateForAuth=getDaemonProfile().getBootState(),headerMap=Object.fromEntries(request.headers.entries());if(stateForAuth==="ready"){let provided=getApiKeyFromHeaders(headerMap);if(!isValidAgentApiKey(provided))return set.status=401,{success:!1,state:stateForAuth,error:"Unauthorized: API key required to reconfigure"}}else{let apiKey=getApiKeyFromHeaders(headerMap),pairing=getPairingTokenFromHeaders(headerMap),apiKeyOk=isValidAgentApiKey(apiKey),pairingOk=isValidPairingToken(pairing);if(!apiKeyOk&&!pairingOk)return set.status=401,{success:!1,state:stateForAuth,error:"Unauthorized: provide x-agent-api-key OR x-pairing-token for bootstrap"}}let{clientId,clientSecret,workspaceId,globalGatewayUrl,workspaceGatewayUrl}=body;if(!clientId||!clientSecret||!workspaceId||!globalGatewayUrl||!workspaceGatewayUrl)return set.status=400,{success:!1,state:getDaemonProfile().getBootState(),error:"clientId, clientSecret, workspaceId, globalGatewayUrl, and workspaceGatewayUrl are required"};let safeGlobalGatewayUrl,safeWorkspaceGatewayUrl;try{safeGlobalGatewayUrl=assertAllowedGatewayUrl(globalGatewayUrl,"globalGatewayUrl"),safeWorkspaceGatewayUrl=assertAllowedGatewayUrl(workspaceGatewayUrl,"workspaceGatewayUrl")}catch(err){return set.status=400,{success:!1,state:getDaemonProfile().getBootState(),error:err instanceof Error?err.message:String(err)}}{let ctx=currentProfileOrNull()??getDaemonProfile();try{await writeAgentConfigAwaitSecrets({clientId,clientSecret,workspaceId,organizationId:body.organizationId,globalGatewayUrl:safeGlobalGatewayUrl,workspaceGatewayUrl:safeWorkspaceGatewayUrl,agentRecordId:body.agentRecordId,scopes:body.scopes},{dir:ctx.dataDir,scope:ctx.name})}catch(err){return set.status=500,{success:!1,state:getDaemonProfile().getBootState(),error:`Failed to persist gateway credentials to secrets backend: ${err instanceof Error?err.message:String(err)}`}}}let state=getDaemonProfile().getBootState();if(state==="ready"){let db=getDb();if(db){if(await db.setConfig("gateway-auth:clientId",clientId),await db.setConfig("gateway-auth:clientSecret",clientSecret),await db.setConfig("gateway-auth:workspaceId",workspaceId),body.organizationId)await db.setConfig("gateway-auth:organizationId",body.organizationId);if(await db.setConfig("gateway-auth:globalGatewayUrl",safeGlobalGatewayUrl),await db.setConfig("gateway-auth:workspaceGatewayUrl",safeWorkspaceGatewayUrl),body.agentRecordId)await db.setConfig("gateway-auth:agentRecordId",body.agentRecordId)}return gatewayClient.configure({globalGatewayUrl:safeGlobalGatewayUrl,workspaceGatewayUrl:safeWorkspaceGatewayUrl,clientId,clientSecret,workspaceId,organizationId:body.organizationId}),getDaemonProfile().logger.info("preconfig","Gateway credentials re-configured (agent was already ready)"),{success:!0,state,message:"Gateway auth re-configured"}}return getDaemonProfile().logger.info("preconfig","Gateway credentials received \u2014 beginning finalize (background)"),invalidatePairingToken(),runFinalize({clientId,clientSecret,workspaceId,organizationId:body.organizationId,globalGatewayUrl:safeGlobalGatewayUrl,workspaceGatewayUrl:safeWorkspaceGatewayUrl},{agentRecordId:body.agentRecordId,scopes:body.scopes}).catch((err)=>{getDaemonProfile().logger.error("preconfig","Background finalize threw",{error:err instanceof Error?err.message:String(err)})}),set.status=202,{success:!0,state:getDaemonProfile().getBootState(),agentRecordId:body.agentRecordId,message:"Gateway credentials accepted; finalize is running in the background. Poll /health/ready for completion."}},{body:t.Object({clientId:t.Optional(t.String()),clientSecret:t.Optional(t.String()),workspaceId:t.Optional(t.String()),organizationId:t.Optional(t.String()),globalGatewayUrl:t.Optional(t.String()),workspaceGatewayUrl:t.Optional(t.String()),scopes:t.Optional(t.Array(t.String())),agentRecordId:t.Optional(t.String())})})}import{existsSync as existsSync2,mkdirSync as mkdirSync2,rmSync,readdirSync}from"fs";import{join as join3}from"path";function profileDir(name){return join3(getVibecontrolsProductDir(),"agents",validateAgentName(name))}function listProfileNames(){let dir=join3(getVibecontrolsProductDir(),"agents");if(!existsSync2(dir))return[];return readdirSync(dir,{withFileTypes:!0}).filter((entry)=>entry.isDirectory()).map((entry)=>entry.name).filter((name)=>/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(name))}function profileDataDir(name){return join3(getVibecontrolsProductDir(),"agents",validateAgentName(name))}function readConfigForProfile(name){return readAgentConfig({dir:profileDataDir(name)})}function writeConfigForProfile(name,cfg){return writeAgentConfig(cfg,{dir:profileDataDir(name),scope:validateAgentName(name)})}function summarize(name){let defaultName=getDefaultProfile(),instance=getInstances().find((p)=>p.name===name),ctx=profileRegistry.get(name),summary={name,isDefault:name===defaultName,isRunning:instance?.status==="running",attached:ctx!==void 0,bootState:ctx?ctx.getBootState():null,dataDir:ctx?ctx.dataDir:null};if(instance)summary.port=instance.port,summary.pid=instance.pid,summary.startTime=instance.startTime;if(existsSync2(profileDir(name)))summary.config=redactUnknownSecrets(readConfigForProfile(name));return summary}function createProfileManagementRoutes(deps={}){let sm=deps.serviceManagerFactory?deps.serviceManagerFactory():new ServiceManager;return new Elysia({prefix:"/api/profiles"}).get("/",()=>{let onDisk=listProfileNames(),defaultName=getDefaultProfile(),names=new Set(onDisk);return names.add(defaultName),[...names].sort().map((n)=>summarize(n))}).post("/",async({body,set})=>{try{let name=validateAgentName(body.name),dir=profileDir(name);if(existsSync2(dir))return set.status=409,{error:"exists",message:`Profile '${name}' already exists.`};let seed;if(body.copyFrom){let src=validateAgentName(body.copyFrom);if(!existsSync2(profileDir(src)))return set.status=400,{error:"copy-source-missing",message:`Profile '${src}' does not exist.`};seed=readConfigForProfile(src),delete seed["static-api-key"],delete seed.agentRecordId,delete seed.apiKeys}else seed=getAgentConfigSeed();if(body.gatewayUrl)seed.globalGatewayUrl=body.gatewayUrl;if(body.workspaceGatewayUrl)seed.workspaceGatewayUrl=body.workspaceGatewayUrl;if(body.clientId)seed.clientId=body.clientId;if(body.clientSecret)seed.clientSecret=body.clientSecret;if(body.workspaceId)seed.workspaceId=body.workspaceId;if(body.organizationId)seed.organizationId=body.organizationId;mkdirSync2(dir,{recursive:!0}),writeConfigForProfile(name,seed);try{getOrCreateProfile(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process attach failed for '${name}'`,{error:String(err)})}if(getDaemonProfile().logger.info("profile-mgmt",`Created profile '${name}' at ${dir}`),body.start){let startCfg={name,port:body.port??0,daemon:!0,dbPath:getProfileDataDir(),profile:name};await sm.startDaemon(startCfg)}return set.status=201,summarize(name)}catch(err){return set.status=400,{error:"create-failed",message:err instanceof Error?err.message:String(err)}}},{body:t.Object({name:t.String(),gatewayUrl:t.Optional(t.String()),workspaceGatewayUrl:t.Optional(t.String()),clientId:t.Optional(t.String()),clientSecret:t.Optional(t.String()),workspaceId:t.Optional(t.String()),organizationId:t.Optional(t.String()),copyFrom:t.Optional(t.String()),start:t.Optional(t.Boolean()),port:t.Optional(t.Number())})}).get("/:name",({params,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};return summarize(name)}catch(err){return set.status=400,{error:"bad-request",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()})}).put("/:name",({params,body,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};let forbidden=["static-api-key","apiKeys","agentRecordId"];for(let key of forbidden)if(key in body)return set.status=400,{error:"forbidden-field",message:`Field '${key}' is managed via dedicated commands (vibe key rotate / vibe profile create). It cannot be patched here.`};let next={...readConfigForProfile(name),...body};return writeConfigForProfile(name,next),summarize(name)}catch(err){return set.status=400,{error:"update-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()}),body:t.Record(t.String(),t.Any())}).delete("/:name",async({params,body,query,set})=>{try{let name=validateAgentName(params.name);if(name===getDefaultProfile())return set.status=409,{error:"is-default",message:`Cannot delete active default profile '${name}'. Switch to another profile first.`};let purge=query?.purge==="true"||query?.purge==="1",hasDir=existsSync2(profileDir(name)),inRegistry=profileRegistry.has(name);if(!hasDir&&!inRegistry)return set.status=404,{error:"not-found",message:`No profile '${name}'.`};if(hasDir&&!purge&&!body?.confirm)return set.status=400,{error:"confirm-required",message:"Pass { confirm: true } in the body or `?purge=true` in the query to delete the on-disk dataDir (destructive)."};try{await sm.stop(name)}catch{}try{await profileRegistry.destroy(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process detach failed for '${name}'`,{error:String(err)})}if(hasDir)if(body?.keepConfig)for(let entry of readdirSync(profileDir(name))){if(entry==="config.json")continue;rmSync(join3(profileDir(name),entry),{recursive:!0,force:!0})}else rmSync(profileDir(name),{recursive:!0,force:!0});return getDaemonProfile().logger.info("profile-mgmt",`Deleted profile '${name}'`),{ok:!0,deleted:name,keptConfig:!!body?.keepConfig,detached:inRegistry,purged:hasDir}}catch(err){return set.status=400,{error:"delete-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()}),query:t.Optional(t.Object({purge:t.Optional(t.String())})),body:t.Optional(t.Object({confirm:t.Optional(t.Boolean()),keepConfig:t.Optional(t.Boolean())}))}).post("/:name/switch",({params,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};return setDefaultProfile(name),getDaemonProfile().logger.info("profile-mgmt",`Default profile switched to '${name}'`),{ok:!0,defaultProfile:name}}catch(err){return set.status=400,{error:"switch-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()})}).post("/:name/start",async({params,body,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};try{getOrCreateProfile(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process attach failed for '${name}'`,{error:String(err)})}if(!getInstances().some((p)=>p.name===name&&p.status==="running")){let startCfg={name,port:body?.port??0,daemon:!0,dbPath:getProfileDataDir(),profile:name};await sm.startDaemon(startCfg)}return summarize(name)}catch(err){return set.status=500,{error:"start-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()}),body:t.Optional(t.Object({port:t.Optional(t.Number())}))}).post("/:name/stop",async({params,set})=>{try{let name=validateAgentName(params.name);try{await sm.stop(name)}catch{}try{await profileRegistry.destroy(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process detach failed for '${name}'`,{error:String(err)})}return summarize(name)}catch(err){return set.status=500,{error:"stop-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()})})}function buildProfileStats(){let rss=process.memoryUsage(),ru=process.resourceUsage(),totalCpuUserMs=ru.userCPUTime/1000,totalCpuSystemMs=ru.systemCPUTime/1000,totalCpuMs=totalCpuUserMs+totalCpuSystemMs,profiles=profileRegistry.list().map((ctx)=>{let childPidCount;try{childPidCount=listTrackedByOwner(ctx.spawnOwnerKey).length}catch{childPidCount=0}return{name:ctx.name,status:ctx.getBootState(),childPidCount,cpuMs:totalCpuMs,heapBytes:rss.heapUsed,dataDir:ctx.dataDir}});return{process:{pid:process.pid,uptimeSeconds:process.uptime(),cpuUserMs:totalCpuUserMs,cpuSystemMs:totalCpuSystemMs,heapUsedBytes:rss.heapUsed,heapTotalBytes:rss.heapTotal,rssBytes:rss.rss},profiles,notes:{perProfileCpu:"deferred",perProfileHeap:"deferred"}}}function createProfileStatsRoutes(){return new Elysia({prefix:"/api/profile-stats"}).get("/",()=>buildProfileStats())}function withTimeout(p,ms){return Promise.race([p,new Promise((_,reject)=>setTimeout(()=>reject(Error(`timeout after ${ms}ms`)),ms))])}function isEnoent(err){let msg=err instanceof Error?err.message:String(err);return/ENOENT/.test(msg)||/no such file or directory/i.test(msg)}async function runDbProbe(db){let probeKey=`__health__:probe:${typeof crypto<"u"&&typeof crypto.randomUUID==="function"?crypto.randomUUID():`${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`}`,value=String(Date.now());if(await withTimeout(db.setConfig(probeKey,value),2000),await withTimeout(db.getConfig(probeKey),2000)!==value)throw Error("round-trip mismatch");try{await withTimeout(db.deleteConfig(probeKey),500)}catch{}}async function checkDb(db,dbPath){if(!db)return{ok:!1,message:"database not opened"};let start=performance.now();try{return await runDbProbe(db),{ok:!0,durationMs:performance.now()-start}}catch(err){if(dbPath&&isEnoent(err))try{return(await import("fs")).mkdirSync(dbPath,{recursive:!0}),await runDbProbe(db),{ok:!0,message:`recovered: dbPath ${dbPath} was missing \u2014 recreated`,durationMs:performance.now()-start}}catch(retryErr){return{ok:!1,message:`ENOENT on dbPath ${dbPath}: ${retryErr instanceof Error?retryErr.message:String(retryErr)}`,durationMs:performance.now()-start}}return{ok:!1,message:err instanceof Error?err.message:String(err),durationMs:performance.now()-start}}}function checkProvider(registry,type){if(!registry)return{ok:!1,message:"no service registry"};let awaiting=getDaemonProfile().getBootState()==="awaiting-config";if(type==="tunnel"){let p=registry.getProvider("tunnel");if(!p&&process.env.AGENT_TUNNEL==="false")return{ok:!0,message:"tunnel disabled via AGENT_TUNNEL=false"};if(p)return{ok:!0,message:`provider: ${p.name}`};return{ok:!1,message:awaiting?"agent is awaiting-config \u2014 POST OAuth credentials to /api/agent/gateway-auth to register tunnel provider":"no tunnel provider registered"}}if(type==="session"){let p=registry.getProvider("session");if(p)return{ok:!0,message:`provider: ${p.name}`};return{ok:!1,message:awaiting?"agent is awaiting-config \u2014 POST OAuth credentials to /api/agent/gateway-auth to register session provider":"no session provider registered"}}return{ok:!0}}async function checkPluginHealth(pm){if(!pm)return{};let results={};for(let plugin of pm.getAllPlugins()){let fn=plugin.getHealth;if(typeof fn!=="function")continue;let start=performance.now();try{let out=await withTimeout(Promise.resolve(fn.call(plugin)),2000);results[plugin.name]={ok:out?.ok!==!1,message:out?.message,durationMs:performance.now()-start}}catch(err){results[plugin.name]={ok:!1,message:err instanceof Error?err.message:String(err),durationMs:performance.now()-start}}}return results}function createHealthRoutes(depsOrRegistry){let deps=depsOrRegistry&&"serviceRegistry"in depsOrRegistry?depsOrRegistry:{serviceRegistry:depsOrRegistry};return new Elysia({prefix:"/health"}).get("/",()=>{return{status:"ok",lifecycleState:deps.serviceRegistry?.getService("agent","lifecycle")?.getState()??"running",bootState:getDaemonProfile().getBootState(),timestamp:new Date().toISOString(),uptime:process.uptime()}}).get("/live",()=>({status:"ok"})).get("/ready",async({set})=>{let lifecycleState=deps.serviceRegistry?.getService("agent","lifecycle")?.getState()??"running",bootState=getDaemonProfile().getBootState(),components={};components.boot={ok:bootState==="ready",message:bootState==="ready"?void 0:`boot state ${bootState}`},components.lifecycle={ok:lifecycleState==="running",message:lifecycleState==="running"?void 0:`lifecycle ${lifecycleState}`},components.db=await checkDb(deps.getDb?.()??null,deps.getDbPath?.()??null),components.tunnel=checkProvider(deps.serviceRegistry,"tunnel"),components.session=checkProvider(deps.serviceRegistry,"session");let plugins=await checkPluginHealth(deps.getPluginManager?.()??null),allOk=Object.values(components).every((r)=>r.ok)&&Object.values(plugins).every((r)=>r.ok);if(!allOk)set.status=503;let reasons=bootState==="degraded"?getDaemonProfile().getDegradedReasons():[],daemonName=getDaemonProfile().name,profilesMap={};for(let ctx of profileRegistry.list())profilesMap[ctx.name]={bootState:ctx.getBootState(),ok:ctx.getBootState()==="ready",degradedReasons:ctx.getDegradedReasons()};let lastFinalizeError=getDaemonProfile().getLastFinalizeError(),body={status:allOk?"ok":"degraded",lifecycleState,bootState,daemon:{profile:daemonName,bootState,ok:bootState==="ready"},profiles:profilesMap,components,plugins,degradedReasons:reasons,lastFinalizeError,timestamp:new Date().toISOString()};if(!allOk)getDaemonProfile().logger.warn("health","Readiness probe failed",{components,plugins,bootState});return body})}var inflight=0,totalServed=0;function createInflightTracker(){return new Elysia({name:"inflight-tracker"}).onRequest(()=>{inflight+=1}).onAfterResponse(()=>{inflight=Math.max(0,inflight-1),totalServed+=1})}function createStatsRoutes(){return new Elysia({prefix:"/api/stats"}).get("/inflight",()=>({inflight:Math.max(0,inflight-1),totalServed}))}var ALLOWED_COMMANDS=Object.freeze(["code","code-insiders","codium","cursor","idea","pycharm","webstorm","goland","phpstorm","rubymine","clion","rider","zed","subl","vim","nvim","emacs","neovide","fleet","helix"]),ALLOWED_SET=new Set(ALLOWED_COMMANDS),ALLOWED_ARG_FLAGS=new Set(["--new-window","-n","--reuse-window","-r","--wait","-w","--add","--goto"]);function which(cmd){try{return Bun.which(cmd)??null}catch{return null}}function createEditorRoutes(){return new Elysia({prefix:"/api/editor"}).get("/allowlist",()=>({commands:[...ALLOWED_COMMANDS]})).get("/detect",async()=>{let results=ALLOWED_COMMANDS.map((cmd)=>[cmd,which(cmd)]),available=[],paths={};for(let[cmd,p]of results)if(p)available.push(cmd),paths[cmd]=p;return{available,paths}}).post("/open",async({body,set})=>{let{command,path:rawPath,args=[]}=body;if(!ALLOWED_SET.has(command))return set.status=400,{ok:!1,error:`Command '${command}' is not in the IDE allowlist`};for(let a of args)if(typeof a!=="string"||!ALLOWED_ARG_FLAGS.has(a))return set.status=400,{ok:!1,error:`Argument '${a}' is not in the flag allowlist`};let resolvedPath;try{resolvedPath=(await resolveSafePath(rawPath,{mustExist:!0})).realPath}catch(err){return set.status=400,{ok:!1,error:`Invalid path: ${err instanceof Error?err.message:String(err)}`}}let resolvedCmd=which(command);if(!resolvedCmd)return set.status=404,{ok:!1,error:`'${command}' is not installed or not on PATH on this agent`};try{let child=spawnTracked({owner:`editor:${command}`,cmd:[resolvedCmd,...args,resolvedPath],env:process.env,stdout:"ignore",stderr:"ignore",stdin:"ignore",meta:{command,path:resolvedPath}});return getDaemonProfile().logger.info("editor",`Launched ${command} on ${resolvedPath}`,{pid:child.pid}),getDaemonProfile().audit.emit("agent","editor.open",{command,path:resolvedPath,pid:child.pid}),{ok:!0,command,resolvedPath,pid:child.pid}}catch(err){return set.status=500,{ok:!1,error:`Failed to spawn editor: ${err instanceof Error?err.message:String(err)}`}}},{body:t.Object({command:t.String({minLength:1,maxLength:40}),path:t.String({minLength:1,maxLength:4096}),args:t.Optional(t.Array(t.String({maxLength:32})))})})}var REGISTRY_KEY=Symbol.for("@vibecontrols/plugin-sdk:contextProviders@1");function getRegistry(){let slots=globalThis,existing=slots[REGISTRY_KEY];if(existing)return existing;let created=new Map;return slots[REGISTRY_KEY]=created,created}function listContextProviders(){return Array.from(getRegistry().values())}function safeHost(maybeUrl){if(!maybeUrl)return null;try{return new URL(maybeUrl).host}catch{return null}}async function buildAgentSelfContext(deps,args){let pm=deps.getPluginManager(),loadedPlugins=pm?pm.getPluginDetails().map((p)=>({pluginName:p.pluginName,packageName:p.packageName,version:p.version,tags:p.tags??[]})):[],mem=process.memoryUsage(),tunnelHost=safeHost(deps.getTunnelHost?.()??null);return{pluginName:"agent",description:"vibecontrols-agent runtime self-report",generatedAt:new Date().toISOString(),data:{profile:args.profile,vibeId:args.vibeId??null,runtime:{bunVersion:typeof Bun<"u"?Bun.version:null,nodeVersion:process.versions.node??null,platform:process.platform,arch:process.arch,pid:process.pid,uptimeSeconds:Math.round(process.uptime()),memory:{rssMB:Math.round(mem.rss/1048576),heapUsedMB:Math.round(mem.heapUsed/1048576)}},agent:{version:deps.getAgentVersion?.()??null,profileRegistered:getVibecontrolsProfile(),tunnelHost},plugins:{count:loadedPlugins.length,items:loadedPlugins}}}}var DEFAULT_PROVIDER_TIMEOUT_MS=2000,SINGLE_FLIGHT_TTL_MS=1000;async function withTimeout2(promise,timeoutMs){let timer,timeout=new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error(`timeout after ${timeoutMs}ms`)),timeoutMs)});try{return await Promise.race([promise,timeout])}finally{if(timer)clearTimeout(timer)}}async function runOne(provider,args){let start=performance.now(),timeoutMs=provider.timeoutMs??DEFAULT_PROVIDER_TIMEOUT_MS;try{let data=await withTimeout2(provider.getContext(args),timeoutMs);if(!data.generatedAt)data.generatedAt=new Date().toISOString();return{ok:!0,data,durationMs:Math.round(performance.now()-start)}}catch(err){return{ok:!1,error:err.message??String(err),durationMs:Math.round(performance.now()-start)}}}class ContextAggregator{deps;inflight=new Map;constructor(deps){this.deps=deps}async aggregate(args){let key=`${args.profile}::${args.vibeId??""}`,existing=this.inflight.get(key);if(existing&&Date.now()-existing.ts<SINGLE_FLIGHT_TTL_MS)return existing.promise;let promise=this.doAggregate(args).finally(()=>{setTimeout(()=>{let entry=this.inflight.get(key);if(entry&&entry.promise===promise)this.inflight.delete(key)},SINGLE_FLIGHT_TTL_MS).unref?.()});return this.inflight.set(key,{ts:Date.now(),promise}),promise}async selfContext(args){return buildAgentSelfContext(this.deps,args)}async pluginContext(name,args){let provider=listContextProviders().find((p)=>p.name===name);if(!provider)return null;return runOne(provider,args)}async pluginsContext(args){let providers=listContextProviders(),settled=await Promise.allSettled(providers.map((p)=>runOne(p,args))),out={};return providers.forEach((p,i)=>{let r=settled[i];if(r&&r.status==="fulfilled")out[p.name]=r.value;else if(r&&r.status==="rejected")out[p.name]={ok:!1,error:r.reason instanceof Error?r.reason.message:String(r.reason),durationMs:0}}),out}async doAggregate(args){let[agent,plugins]=await Promise.all([this.selfContext(args),this.pluginsContext(args)]);return{agent,plugins}}}function parseVibeId(query){let raw=query.vibeId;if(typeof raw!=="string")return;let v=raw.trim();return v.length>0&&v.length<=128?v:void 0}function createContextRoutes(deps){let aggregator=new ContextAggregator(deps);return new Elysia({prefix:"/api/agent/context"}).get("/",async({params,query})=>{let profile=params?.profile??"default",vibeId=parseVibeId(query);return aggregator.aggregate({profile,vibeId})},{query:t.Object({vibeId:t.Optional(t.String())})}).get("/agent",async({params,query})=>{let profile=params?.profile??"default",vibeId=parseVibeId(query);return aggregator.selfContext({profile,vibeId})},{query:t.Object({vibeId:t.Optional(t.String())})}).get("/plugins",async({params,query})=>{let profile=params?.profile??"default",vibeId=parseVibeId(query);return aggregator.pluginsContext({profile,vibeId})},{query:t.Object({vibeId:t.Optional(t.String())})}).get("/plugins/:name",async({params,query,set})=>{let profile=params?.profile??"default",name=params?.name;if(!name)return set.status=400,{error:"missing plugin name"};let vibeId=parseVibeId(query),entry=await aggregator.pluginContext(name,{profile,vibeId});if(!entry)return set.status=404,{error:`no context provider registered under name "${name}"`};return entry},{params:t.Object({name:t.String()}),query:t.Object({vibeId:t.Optional(t.String())})})}var FINALIZE_EXEMPT_PREFIXES=["/health","/api/agent/status","/api/agent/version","/api/agent/identity","/api/agent/api-key","/api/agent/tunnel","/api/agent/system","/api/agent/gateway-auth","/api/stats","/metrics","/api/profiles","/api/profile-stats"];function isFinalizeExempt(path){return FINALIZE_EXEMPT_PREFIXES.some((p)=>path.startsWith(p))}var UUID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function normalizeRouteLabel(path){return path.split("/").map((seg)=>{if(!seg)return seg;if(/^\d+$/.test(seg))return":id";if(UUID_RE.test(seg))return":id";if(seg.length>=24&&/^[A-Za-z0-9_-]+$/.test(seg))return":id";return seg}).join("/")}async function createApp(options){let{port,host,dbPath,logLevel,corsOrigin}=options,effectiveDbPath=dbPath??getProfileDataDir();try{(await import("fs")).mkdirSync(effectiveDbPath,{recursive:!0})}catch(err){throw getDaemonProfile().logger.error("app","Failed to create dbPath dir at boot",{dbPath:effectiveDbPath,error:String(err)}),Error(`Cannot create dbPath ${effectiveDbPath}: ${err instanceof Error?err.message:String(err)}`,{cause:err})}if(logLevel)getDaemonProfile().logger.setLevel(logLevel);(async()=>{let{logShipper}=await import("./log-shipper-9rtt4y1s.js");logShipper.init()})(),(async()=>{let version=await getPackageVersion();await getMetricsRegistry({version,profile:getVibecontrolsProfile()})})();try{let{checkDependencies}=await import("./bootstrap.service-fknk5beq.js"),deps=checkDependencies(),missingRequired=deps.filter((d)=>d.required&&!d.installed),missingOptional=deps.filter((d)=>!d.required&&!d.installed);if(missingRequired.length>0)getDaemonProfile().logger.error("app",`Missing required system deps: ${missingRequired.map((d)=>d.name).join(", ")}. Run \`vibe setup\` to install.`);if(missingOptional.length>0)getDaemonProfile().logger.warn("app",`Missing optional system deps: ${missingOptional.map((d)=>d.name).join(", ")}. Some plugins may fail their prereqs check.`)}catch(err){getDaemonProfile().logger.warn("app","Boot-time dependency check failed",{error:String(err)})}try{getDaemonProfile().audit.runRetention()}catch(err){getDaemonProfile().logger.warn("app","Audit-log retention sweep failed",{error:String(err)})}let serviceRegistry=new ServiceRegistry;startBootstrapTunnel(serviceRegistry,{port});let CORS_METHODS="GET, POST, PUT, PATCH, DELETE, OPTIONS",CORS_HEADERS="Content-Type, x-agent-api-key, Authorization, x-requested-with",configuredCorsOrigins=Array.isArray(corsOrigin)?corsOrigin:(corsOrigin??"").split(",").map((origin)=>origin.trim()).filter(Boolean),trustedProductionOrigins=new Set(["https://vibecontrols.com","https://app.vibecontrols.com","https://alphaapp.vibecontrols.com","https://burdenoff.com","https://app.burdenoff.com","https://alphaapp.burdenoff.com"]),isAllowedOrigin=(origin)=>{try{let url=new URL(origin);if(configuredCorsOrigins.includes(origin))return!0;if(configuredCorsOrigins.includes("*"))return!1;return url.hostname==="localhost"||url.hostname==="127.0.0.1"||url.hostname==="0.0.0.0"||url.hostname.endsWith(".local.burdenoff.com")||trustedProductionOrigins.has(url.origin)}catch{return!1}},db=null,pluginManager=null,hostServices=null,lifecycle=null,pluginPublicPaths=()=>[],pluginRoutesApp=null,uiServerApp=null,finalizeInFlight=null,SHUTDOWN_PHASE_MS=5000,runPhase=async(name,fn)=>{let started=Date.now();try{await Promise.race([Promise.resolve(fn()),new Promise((_,reject)=>setTimeout(()=>reject(Error(`shutdown phase ${name} exceeded ${SHUTDOWN_PHASE_MS}ms`)),SHUTDOWN_PHASE_MS))]),getDaemonProfile().logger.info("app",`shutdown.phase ${name} complete`,{ms:Date.now()-started})}catch(err){getDaemonProfile().logger.warn("app",`shutdown.phase ${name} failed`,{error:String(err),ms:Date.now()-started})}},stopFn=async(context)=>{if(getDaemonProfile().logger.info("app","Agent shutdown starting",{reason:context?.reason??"shutdown"}),await runPhase("plugins",async()=>{if(pluginManager)await pluginManager.dispatchServerStop(context)}),await runPhase("bootstrap-tunnel",()=>stopBootstrapTunnel()),await runPhase("tracked-subprocesses",async()=>{let{killAllTracked}=await import("./subprocess-vks78xmn.js");await killAllTracked()}),context?.reason!=="reload")await runPhase("db-close",async()=>{if(db)await db.close()});getDaemonProfile().logger.close(),getDaemonProfile().logger.info("app","Agent server stopped")},finalize=async(creds,opts)=>{if(getDaemonProfile().getBootState()==="ready")return{ok:!0};if(finalizeInFlight)return finalizeInFlight;return finalizeInFlight=(async()=>{getDaemonProfile().setBootState("initializing");try{let safeCreds={...creds,globalGatewayUrl:assertAllowedGatewayUrl(creds.globalGatewayUrl,"globalGatewayUrl"),workspaceGatewayUrl:assertAllowedGatewayUrl(creds.workspaceGatewayUrl,"workspaceGatewayUrl")},encryptionKey=await fetchEncryptionKey(safeCreds),resolvedDbPath=effectiveDbPath,cfgForStorage=readAgentConfig({dir:getDaemonProfile().dataDir}),storageAdapterName=cfgForStorage.storageAdapter??process.env.VIBE_STORAGE_ADAPTER??void 0,storageAdapterOptions=cfgForStorage.storageOptions;try{db=await createAgentDatabase({dbPath:resolvedDbPath,encryptionKey,adapterName:storageAdapterName,adapterOptions:storageAdapterOptions})}catch(openErr){let message=openErr instanceof Error?openErr.message:String(openErr);getDaemonProfile().logger.error("app","Storage open failed; quarantining and retrying once",{error:message,path:resolvedDbPath});try{let fs=await import("fs");if(fs.existsSync(resolvedDbPath)){let ts=new Date().toISOString().replace(/[:.]/g,"-"),broken=`${resolvedDbPath}.broken-${ts}`;fs.renameSync(resolvedDbPath,broken),getDaemonProfile().logger.warn("app",`Quarantined storage to ${broken} \u2014 fresh DB will be created`)}db=await createAgentDatabase({dbPath:resolvedDbPath,encryptionKey,adapterName:storageAdapterName,adapterOptions:storageAdapterOptions})}catch(retryErr){let retryMsg=retryErr instanceof Error?retryErr.message:String(retryErr);throw getDaemonProfile().recordDegradedReason("storage",retryMsg),Error(`Storage open failed twice; agent is degraded: ${retryMsg}`,{cause:retryErr})}}if(await writeAgentConfigAwaitSecrets({clientId:safeCreds.clientId,clientSecret:safeCreds.clientSecret,workspaceId:safeCreds.workspaceId,organizationId:safeCreds.organizationId,globalGatewayUrl:safeCreds.globalGatewayUrl,workspaceGatewayUrl:safeCreds.workspaceGatewayUrl,agentRecordId:opts?.agentRecordId,scopes:opts?.scopes},{dir:getDaemonProfile().dataDir,scope:getDaemonProfile().name}),await db.setConfig("gateway-auth:clientId",safeCreds.clientId),await db.setConfig("gateway-auth:clientSecret",safeCreds.clientSecret),await db.setConfig("gateway-auth:globalGatewayUrl",safeCreds.globalGatewayUrl),await db.setConfig("gateway-auth:workspaceGatewayUrl",safeCreds.workspaceGatewayUrl),await db.setConfig("gateway-auth:workspaceId",safeCreds.workspaceId),safeCreds.organizationId)await db.setConfig("gateway-auth:organizationId",safeCreds.organizationId);if(opts?.agentRecordId)await db.setConfig("gateway-auth:agentRecordId",opts.agentRecordId);serviceRegistry.db=db,await serviceRegistry.hydrateDefaultsFromDb(),pluginManager=new PluginManager(db),await pluginManager.loadCorePlugins();try{await pluginManager.loadAll()}catch(err){getDaemonProfile().logger.warn("app","Failed to load some external plugins",{error:String(err)})}let autoInstallDisabled=process.env.VIBE_SKIP_AUTO_INSTALL==="1"||await db.getConfig("plugins:auto-install-disabled");if(autoInstallDisabled!=="true"&&autoInstallDisabled!==!0)try{let installed=await pluginManager.ensureDefaultPlugins((message)=>getDaemonProfile().logger.info("app",`[auto-install] ${message}`));if(installed.length>0)getDaemonProfile().logger.info("app",`Auto-installed ${installed.length} default plugin(s): ${installed.map((p)=>p.packageName).join(", ")}`)}catch(err){getDaemonProfile().logger.warn("app","Failed to auto-install default plugins",{error:String(err)})}getDaemonProfile().logger.info("app",`Plugins loaded: ${pluginManager.getPluginDetails().length} total (${pluginManager.getPluginDetails().filter((p)=>p.isCore).length} core)`);try{let details=pluginManager.getPluginDetails(),byStatus={loaded:details.filter((p)=>p.loaded).length,failed:details.filter((p)=>!p.loaded).length};setPluginsLoaded(byStatus)}catch{}let storageProvider={async get(namespace,key){return await db.getPluginState(namespace,key)??null},async set(namespace,key,value){await db.setPluginState(namespace,key,value)},async delete(namespace,key){return db.deletePluginState(namespace,key)},async list(namespace){return await db.getAllPluginState(namespace)},async deleteAll(namespace){return db.deleteAllPluginState(namespace)}},packageVersion=await getPackageVersion();hostServices={storage:storageProvider,logger:getDaemonProfile().logger,serviceRegistry,getProvider:(type)=>serviceRegistry.getProvider(type),getAgentBaseUrl:()=>process.env.AGENT_URL||`http://localhost:${port}`,getAgentVersion:()=>packageVersion,validateApiKey:(key)=>isValidAgentApiKey(key),setCodeServerSessionValidator:(fn)=>{codeServerSessionValidator=fn},broadcast,workspaceQuery:async(query,variables)=>gatewayClient.workspaceQuery(query,variables),isGatewayConfigured:()=>gatewayClient.isConfigured(),getAgentRecordId:async()=>await db.getConfig("gateway-auth:agentRecordId")??null,getWorkspaceId:async()=>await db.getConfig("gateway-auth:workspaceId")??null,getConfig:async(key)=>await db.getConfig(key)??void 0,getPluginRegistry:()=>getPluginRegistry(),getDataDir:()=>getVibecontrolsDir(),cliContributors:new CliContributorRegistry,audit:{emit:(event,payload)=>getDaemonProfile().audit.emit("agent",event,payload??{})},telemetry:{emit:(event,payload)=>{import("./telemetry-rew0mtj2.js").then(({telemetryService})=>telemetryService.emit(event,payload??{})).catch(()=>{})}},os:getOsAdapter(),iframeBridge:createIframeBridge()},gatewayClient.configure({globalGatewayUrl:safeCreds.globalGatewayUrl,workspaceGatewayUrl:safeCreds.workspaceGatewayUrl,clientId:safeCreds.clientId,clientSecret:safeCreds.clientSecret,workspaceId:safeCreds.workspaceId,organizationId:safeCreds.organizationId});let routeDeps={db,serviceRegistry,pluginManager,broadcast,hostServices};routeDeps.app=app;let pluginRoutesAppInitialized=!1,rebuildPluginSurfaces=()=>{let{routes:pluginRoutes,getPublicPaths,mountPlugin}=createPluginRouter(pluginManager,routeDeps);if(routeDeps.mountPlugin=mountPlugin,pluginPublicPaths=getPublicPaths,markPluginPublicPathsDirty(),pluginRoutesApp=pluginRoutes,uiServerApp=createUIServer(pluginManager),pluginManager&&pluginRoutesAppInitialized&&hostServices)pluginManager.dispatchServerStart(pluginRoutesApp,hostServices).catch((err)=>getDaemonProfile().logger.warn("app","Re-dispatch of onServerStart after plugin reload failed",{error:String(err)}));pluginRoutesAppInitialized=!0};routeDeps.rebuildPluginSurfaces=rebuildPluginSurfaces,rebuildPluginSurfaces(),lifecycle=new LifecycleManager(serviceRegistry,pluginManager,db,app,hostServices,stopFn),serviceRegistry.registerService("agent","lifecycle",lifecycle),getDaemonProfile().attachServices({db,serviceRegistry,pluginManager,lifecycle,hostServices}),mountProfileRoutes(getDaemonProfile(),{rebuildPluginSurfaces,mountPlugin:routeDeps.mountPlugin});try{await getDaemonProfile().keyVault.bindApiKey(getAgentApiKey())}catch(err){getDaemonProfile().logger.warn("app","Failed to bind api key into daemon keyVault",{error:String(err)})}let criticalFailures=[];try{let{failures}=await pluginManager.dispatchServerStart(pluginRoutesApp,hostServices);for(let f of failures)if(isCriticalPlugin(f.plugin)||(f.packageName?isCriticalPlugin(f.packageName):!1))criticalFailures.push(f),getDaemonProfile().recordDegradedReason(f.plugin,f.error);else getDaemonProfile().logger.warn("app",`Non-critical plugin ${f.plugin} failed onServerStart; continuing`,{error:f.error})}catch(err){getDaemonProfile().logger.warn("app","Error during plugin onServerStart dispatch",{error:String(err)})}try{await pluginManager.dispatchServerReady(app,hostServices)}catch(err){getDaemonProfile().logger.warn("app","Error during plugin onServerReady dispatch",{error:String(err)})}if(getDaemonProfile().clearFinalizeError(),criticalFailures.length>0)getDaemonProfile().setBootState("degraded"),getDaemonProfile().logger.error("app","Agent finalized but degraded \u2014 critical plugins failed",{failures:criticalFailures.map((f)=>`${f.plugin}: ${f.error}`)});else getDaemonProfile().setBootState("ready"),getDaemonProfile().logger.info("app","Agent finalized \u2014 state=ready"),getDaemonProfile().audit.emit("agent","gateway-auth.configured",{workspaceId:safeCreds.workspaceId,organizationId:safeCreds.organizationId,agentRecordId:opts?.agentRecordId});return(async()=>{try{if(await pluginManager.provisionDefaultPrereqs(app,hostServices),getDaemonProfile().logger.info("app","Default provider prerequisites provisioned"),(serviceRegistry.listProvidersForType("tunnel")??[]).some((p)=>p.pluginName!=="bootstrap")){let{tryHandoverBootstrapTunnel}=await import("./tunnel-bootstrap-x82wcnt8.js");if(!tryHandoverBootstrapTunnel())stopBootstrapTunnel();serviceRegistry.unregisterProvider("tunnel","bootstrap"),getDaemonProfile().logger.info("app","Bootstrap tunnel handed over to the real tunnel provider")}}catch(err){getDaemonProfile().logger.warn("app","Plugin prerequisite provisioning failed",{error:String(err)})}})(),{ok:!0}}catch(err){getDaemonProfile().setBootState("awaiting-config");let msg=err instanceof Error?err.message:String(err);if(getDaemonProfile().recordFinalizeError(msg),getDaemonProfile().logger.error("app","Finalize failed",{error:msg}),process.env.VIBE_STRICT_KEY_FETCH==="1")getDaemonProfile().logger.error("app","VIBE_STRICT_KEY_FETCH=1 \u2192 aborting daemon (strict mode)",{error:msg}),setTimeout(()=>process.exit(1),50);return{ok:!1,error:msg}}finally{finalizeInFlight=null}})(),finalizeInFlight},corsPlugin=cors({origin:(request)=>{let origin=request.headers.get("origin");return!!origin&&isAllowedOrigin(origin)},credentials:!0,allowedHeaders:CORS_HEADERS.split(",").map((s)=>s.trim()),methods:CORS_METHODS.split(",").map((s)=>s.trim()),exposeHeaders:["content-type","content-length"],maxAge:86400,preflight:!0}),DEFAULT_MAX_BODY_BYTES=10485760,maxBodyEnv=process.env.VIBECONTROLS_MAX_BODY_BYTES,parsedMaxBody=maxBodyEnv?Number.parseInt(maxBodyEnv,10):NaN,maxBodySize=Number.isFinite(parsedMaxBody)&&parsedMaxBody>0?parsedMaxBody:DEFAULT_MAX_BODY_BYTES,appRef={current:null},app=new Elysia({serve:{maxRequestBodySize:maxBodySize}}).onRequest(({request,set})=>{let requestId=extractRequestId(request.headers)??generateRequestId();enterRequestContext({requestId,startedAt:new Date().toISOString()});let headers=set.headers??={};headers["X-Request-Id"]=requestId}).onRequest(({request})=>{try{let url=new URL(request.url).pathname,name=/^\/api\/profiles\/([^/]+)(?:\/|$)/.exec(url)?.[1],ctx=name?profileRegistry.get(name):null;enterProfileContext(ctx??getDaemonProfile())}catch{enterProfileContext(getDaemonProfile())}}).onRequest(({request,set})=>{let url=new URL(request.url).pathname;if(!url.startsWith("/api/"))return;if(url==="/api/profiles"||url.startsWith("/api/profiles/"))return;if(url==="/api/profile-stats"||url.startsWith("/api/profile-stats/"))return;let defaultProfileUrl=new URL(request.url);defaultProfileUrl.pathname=`/api/profiles/default${url.slice(4)}`;try{request.headers.set("x-vc-profile-rewrite","1"),request.headers.set("x-vc-profile-rewrite-target",defaultProfileUrl.pathname)}catch{}return}).derive(()=>({requestId:getRequestId()??generateRequestId()})).use(corsPlugin).use(rateLimitPlugin()).use(createAuthPlugin(()=>pluginPublicPaths())).use(idempotencyPlugin({getDb:()=>currentProfileOrNull()?.db??db})).onBeforeHandle(({request,set})=>{let state=getDaemonProfile().getBootState();if(state==="ready")return;let url=new URL(request.url).pathname;if(isFinalizeExempt(url))return;return set.status=503,{error:"Agent not yet configured",message:"Agent is in '"+state+"' state. POST credentials to /api/agent/gateway-auth to finalize.",state}}).onAfterHandle(({request,set})=>{let url=new URL(request.url).pathname,isNoisy=url==="/health"||url.startsWith("/api/logs/stream")||url.startsWith("/ui/")||url==="/ws/events"||url==="/api/sessions/health-check"||url.startsWith("/terminal/"),status=typeof set.status==="number"?set.status:200,level=isNoisy?"debug":status>=400?"warn":"info";getDaemonProfile().logger[level]("http",`${request.method} ${url} \u2192 ${status}`,{method:request.method,path:url,statusCode:status});try{recordHttpRequest(request.method,normalizeRouteLabel(url),status)}catch{}}).use(createHealthRoutes({serviceRegistry,getDb:()=>currentProfileOrNull()?.db??db,getDbPath:()=>effectiveDbPath,getPluginManager:()=>currentProfileOrNull()?.pluginManager??pluginManager})).use(createMetricsRoutes()).use(createPreConfigRoutes({serviceRegistry,runFinalize:(creds,opts)=>finalize(creds,opts),getDb:()=>currentProfileOrNull()?.db??db})).use(createInflightTracker()).use(createStatsRoutes()).use(createEditorRoutes()).use(createContextRoutes({getPluginManager:()=>currentProfileOrNull()?.pluginManager??pluginManager,getTunnelHost:()=>readTunnelState()?.url??null})).use(createProfileManagementRoutes()).use(createProfileStatsRoutes()).use(createEventsWs()).use(createLogWs()).use(createTerminalProxy(serviceRegistry)).ws("/code-server/*",buildCodeServerBridge(port)).all("/api/profiles/:name/*",async({params,request,set})=>{let hasReqBody=request.method!=="GET"&&request.method!=="HEAD",bodyBytes=hasReqBody?await request.arrayBuffer():void 0,dispatched=await dispatchProfileRequest(params.name,request,bodyBytes);if(dispatched&&dispatched.status!==404)return dispatched;let running=getVibecontrolsProfile(),requested=params.name;if(requested!==running)return set.status=404,{error:"profile-mismatch",running,requested,hint:"This daemon serves a different profile. Call GET /api/profiles to discover the right tunnel."};let u=new URL(request.url),stripped=u.pathname.replace(/^\/api\/profiles\/[^/]+/,"/api"),headers=new Headers(request.headers);headers.set("x-vc-profile-rewrite","1");let init={method:request.method,headers};if(hasReqBody&&bodyBytes!==void 0)init.body=bodyBytes;let rewritten=new Request(`${u.origin}${stripped}${u.search}`,init),target=appRef.current;if(!target)return new Response(JSON.stringify({error:"agent-not-ready"}),{status:503,headers:{"content-type":"application/json"}});return target.handle(rewritten)}).all("/api/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!pluginRoutesApp)return new Response(JSON.stringify({error:"Agent not yet configured",state:getDaemonProfile().getBootState(),message:"Plugin routes are unavailable until the agent reaches state=ready. POST credentials to /api/agent/gateway-auth to finalize."}),{status:503,headers:{"content-type":"application/json"}});try{let apiUiUrl=new URL(request.url),apiUiMatch=/^\/api\/plugins\/([a-z0-9-]{1,64})\/ui(?:\/.*)?$/.exec(apiUiUrl.pathname);if(apiUiMatch&&request.method==="GET"){let plugin=apiUiMatch[1];if(isValidUiPluginName(plugin)){let restAfterUi=apiUiUrl.pathname.slice(`/api/plugins/${plugin}/ui`.length),isBareDoc=restAfterUi===""||restAfterUi==="/",looksLikeNavigation=request.headers.get("sec-fetch-mode")==="navigate";if(isBareDoc||looksLikeNavigation){let cookie=getUiCookieFromRequest(request,plugin);if(!cookie||!verifyUiCookieToken(cookie,plugin))return new Response(renderUiBootstrapHtml(plugin,"/api/profiles/default/agent/ui-exchange-plugin"),{status:200,headers:{"content-type":"text/html; charset=utf-8","Content-Security-Policy":"frame-ancestors 'self' http://localhost:* http://127.0.0.1:* https://localhost:* https://127.0.0.1:* https://*.local.burdenoff.com https://vibecontrols.com https://*.vibecontrols.com https://burdenoff.com https://app.burdenoff.com https://alphaapp.burdenoff.com"}})}}}}catch{}return pluginRoutesApp.handle(request)}).all("/ui/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!uiServerApp)return new Response("Plugin UI not yet available",{status:503});return uiServerApp.handle(request)}).all("/code-server/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!pluginRoutesApp)return new Response("code-server not yet available",{status:503});return pluginRoutesApp.handle(request)}).all("/plan/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!pluginRoutesApp)return new Response("plan provider not yet available",{status:503});return pluginRoutesApp.handle(request)});return appRef.current=app,{app,async start(){app.listen({port,hostname:host}),getDaemonProfile().logger.info("app",`Agent server listening on ${host}:${port}`);try{let list=(process.env.VIBECONTROLS_PROFILES??"").split(",").map((s)=>s.trim()).filter(Boolean),bootName=getDaemonProfile().name,{getOrCreateProfile:getOrCreateProfile2}=await import("./daemon-profile-c7zt62gk.js"),{attachSecondaryProfile}=await import("./secondary-profile-attach-2wnm7ar1.js"),strict=process.env.VIBE_STRICT_KEY_FETCH==="1";for(let name of list){if(name===bootName)continue;try{let ctx=getOrCreateProfile2(name),result=await attachSecondaryProfile(ctx);if(result.ok)getDaemonProfile().logger.info("app",`Secondary profile '${name}' attached (db+services online)`);else if(strict)throw Error(`attachServices for secondary profile '${name}' failed: ${result.error}`);else ctx.recordDegradedReason("attach",result.error??"unknown"),getDaemonProfile().logger.warn("app",`Secondary profile '${name}' is degraded (no creds / attach failed)`,{error:result.error})}catch(err){if(strict)throw getDaemonProfile().logger.error("app",`--strict-key-fetch: secondary profile '${name}' attach failed; aborting`,{error:String(err)}),setTimeout(()=>process.exit(1),50),err;getDaemonProfile().logger.warn("app",`Failed to attach secondary profile '${name}'`,{error:String(err)})}}}catch{}return app},stop:stopFn,finalize,getState:()=>getDaemonProfile().getBootState(),getLifecycle:()=>currentProfileOrNull()?.lifecycle??lifecycle,getDb:()=>currentProfileOrNull()?.db??db,getServiceRegistry:()=>currentProfileOrNull()?.serviceRegistry??serviceRegistry,getPluginManager:()=>currentProfileOrNull()?.pluginManager??pluginManager,getHostServices:()=>currentProfileOrNull()?.hostServices??hostServices}}var codeServerSessionValidator=null;function getCookieValue(cookieHeader,name){if(!cookieHeader)return null;let m=cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));return m?m[1]:null}function buildCodeServerBridge(port){let payloadBytes=(payload)=>typeof payload==="string"?Buffer.byteLength(payload):payload.byteLength;return{open(ws){let wsData=ws.data,apiKey=wsData.query?.apiKey??getApiKeyFromHeaders(wsData.headers??{})??getApiKeyFromHeaders(wsData.request?Object.fromEntries(wsData.request.headers.entries()):{});if(!isValidAgentApiKey(apiKey)){let cookieHeader=wsData.headers?.cookie??wsData.request?.headers.get("cookie")??null,sessionToken=getCookieValue(cookieHeader,"__vibe_cs_session");if(!sessionToken||!codeServerSessionValidator?.(sessionToken)){ws.close(1008,"Unauthorized");return}}let bridgeProfile=getDaemonProfile().name;fetch(`http://127.0.0.1:${port}/api/profiles/${encodeURIComponent(bridgeProfile)}/code-server/status`,{headers:{"x-agent-api-key":getAgentApiKey()}}).then((r)=>r.json()).then((raw)=>{if(wsData._csClosed)return;let status=raw;if(!status.running||!status.port){ws.close(1011,"code-server not running");return}let requestUrl=wsData.request?.url??"/code-server/",upstreamPath;try{let url=new URL(requestUrl,`http://127.0.0.1:${status.port}`);upstreamPath=(url.pathname.replace(/^\/code-server\/?/,"/")||"/")+url.search}catch{upstreamPath="/"}let upstreamWs=new WebSocket(`ws://127.0.0.1:${status.port}${upstreamPath}`),state={upstream:upstreamWs,ready:!1,buffer:[],bufferBytes:0,openTimer:setTimeout(()=>{try{upstreamWs.close(1011,"upstream open timeout")}catch{}try{ws.close(1011,"upstream open timeout")}catch{}},1e4)};wsData._csBridge=state,upstreamWs.addEventListener("open",()=>{clearTimeout(state.openTimer),state.ready=!0;for(let msg of state.buffer)upstreamWs.send(msg);state.buffer.length=0,state.bufferBytes=0}),upstreamWs.addEventListener("message",(event)=>{try{let d=event.data;if(d instanceof ArrayBuffer)ws.send(new Uint8Array(d));else if(d instanceof Blob)d.arrayBuffer().then((ab)=>{try{ws.send(new Uint8Array(ab))}catch{}});else ws.send(d)}catch{}}),upstreamWs.addEventListener("close",(event)=>{clearTimeout(state.openTimer);try{ws.close(event.code||1000,event.reason||"upstream closed")}catch{}}),upstreamWs.addEventListener("error",()=>{clearTimeout(state.openTimer);try{ws.close(1011,"upstream error")}catch{}})}).catch(()=>{ws.close(1011,"Failed to query code-server status")})},message(ws,message){let state=ws.data?._csBridge;if(!state)return;let payload;if(typeof message==="string")payload=message;else if(message instanceof ArrayBuffer)payload=message;else if(message instanceof Uint8Array||Buffer.isBuffer(message)){let copy=new ArrayBuffer(message.byteLength);new Uint8Array(copy).set(new Uint8Array(message.buffer,message.byteOffset,message.byteLength)),payload=copy}else if(typeof message==="object"&&message!==null)payload=JSON.stringify(message);else payload=String(message);if(state.upstream&&state.ready&&state.upstream.readyState===WebSocket.OPEN)try{state.upstream.send(payload)}catch{}else{let nextBytes=payloadBytes(payload);if(state.buffer.length>=256||state.bufferBytes+nextBytes>1048576){try{state.upstream.close(1013,"buffer limit exceeded")}catch{}try{ws.close(1013,"buffer limit exceeded")}catch{}return}state.buffer.push(payload),state.bufferBytes+=nextBytes}},close(ws){ws.data._csClosed=!0;let state=ws.data?._csBridge;if(state?.openTimer)clearTimeout(state.openTimer);if(state?.upstream&&state.upstream.readyState===WebSocket.OPEN)state.upstream.close(1000,"client disconnected")}}}async function getPackageVersion(){try{let{readFileSync:readFileSync3}=await import("fs"),{join:join4,dirname:dirname2}=await import("path"),{fileURLToPath}=await import("url"),__dirname2=dirname2(fileURLToPath(import.meta.url));return JSON.parse(readFileSync3(join4(__dirname2,"..","package.json"),"utf8")).version||"1.0.0"}catch{return"1.0.0"}}
|
|
4
|
+
`)||sid.includes("|"))return set.status=400,{error:"BadRequest",message:"invalid sid"};let expectedPath=`/terminal/${sid}`,verifyResult=verifyIframeTokenWithReason(token,expectedPath);if(!verifyResult.ok)return getDaemonProfile().logger.warn("terminal-exchange","iframe token rejected",{sid,reason:verifyResult.reason}),set.status=401,{error:"Unauthorized",message:"invalid iframe token",code:verifyResult.reason==="expired"?"expired":verifyResult.reason==="replayed"?"replayed":"invalid"};let cookie=issueTerminalCookieToken(sid,600),cookieName=`vt_term_${sid}`;return set.headers["set-cookie"]=`${cookieName}=${cookie.token}; HttpOnly; Secure; SameSite=None; Path=/terminal/${sid}; Max-Age=600`,set.status=204,null},{body:t.Object({sid:t.String({minLength:1}),token:t.String({minLength:1})})}).post("/ui-exchange",({body,set})=>{let{plugin,token}=body;if(!plugin||!token)return set.status=400,{error:"BadRequest",message:"plugin and token required"};if(!isValidUiPluginName(plugin))return set.status=400,{error:"BadRequest",message:"invalid plugin name"};let expectedPath=`/ui/${plugin}`;if(!verifyIframeToken(token,expectedPath))return set.status=401,{error:"Unauthorized",message:"invalid iframe token"};let cookie=issueUiCookieToken(plugin,600),cookieName=`vt_ui_${plugin}`;return set.headers["set-cookie"]=`${cookieName}=${cookie.token}; HttpOnly; Secure; SameSite=None; Path=/ui/${plugin}/; Max-Age=600`,set.status=204,null},{body:t.Object({plugin:t.String({minLength:1}),token:t.String({minLength:1})})}).post("/ui-exchange-plugin",({body,set})=>{let{plugin,token}=body;if(!plugin||!token)return set.status=400,{error:"BadRequest",message:"plugin and token required"};if(!isValidUiPluginName(plugin))return set.status=400,{error:"BadRequest",message:"invalid plugin name"};if(![`/api/plugins/${plugin}/ui`,`/${plugin}`].find((p)=>verifyIframeToken(token,p)))return set.status=401,{error:"Unauthorized",message:"invalid iframe token"};let cookie=issueUiCookieToken(plugin,600),cookieName=`vt_ui_${plugin}`;return set.headers["set-cookie"]=`${cookieName}=${cookie.token}; HttpOnly; Secure; SameSite=None; Path=/api/plugins/${plugin}/ui/; Max-Age=600`,set.status=204,null},{body:t.Object({plugin:t.String({minLength:1}),token:t.String({minLength:1})})}).get("/system",()=>({hostname:os.hostname(),platform:os.platform(),arch:os.arch(),release:os.release(),cpus:os.cpus().length,totalMemory:os.totalmem(),freeMemory:os.freemem(),uptime:os.uptime(),bunVersion:typeof Bun<"u"?Bun.version:void 0,agentVersion:version,homeDir:os.homedir(),cwd:process.cwd(),environment:"production"})).get("/tunnel",async()=>{let url=await getActiveTunnelUrl(serviceRegistry);if(!url)return{tunnelUrl:null,message:"No tunnel provider registered"};return{tunnelUrl:url}}).get("/status",()=>{let providers=["tunnel","session","ai"].filter((type)=>serviceRegistry.hasProvider(type));return{state:getDaemonProfile().getBootState(),status:getDaemonProfile().getBootState(),version,providers,timestamp:new Date().toISOString()}}).get("/gateway-auth",()=>({configured:gatewayClient.isConfigured(),config:redactUnknownSecrets(gatewayClient.getConfig()),state:getDaemonProfile().getBootState()})).post("/retry-config",async({set})=>{let{getFinalizeRetryHandle}=await import("./finalize-retry-handle-registry-vbq5qhj1.js"),handle=getFinalizeRetryHandle();if(!handle)return set.status=409,{ok:!1,error:"No finalize retry worker is active. The agent either never had cached credentials to retry with, or it already reached the ready state."};let result=await handle.forceRetryNow();if(!result.ok)set.status=503;return{ok:result.ok,error:result.ok?void 0:result.error,state:getDaemonProfile().getBootState(),worker:handle.getState()}}).post("/gateway-auth",async({body,request,set})=>{let stateForAuth=getDaemonProfile().getBootState(),headerMap=Object.fromEntries(request.headers.entries());if(stateForAuth==="ready"){let provided=getApiKeyFromHeaders(headerMap);if(!isValidAgentApiKey(provided))return set.status=401,{success:!1,state:stateForAuth,error:"Unauthorized: API key required to reconfigure"}}else{let apiKey=getApiKeyFromHeaders(headerMap),pairing=getPairingTokenFromHeaders(headerMap),apiKeyOk=isValidAgentApiKey(apiKey),pairingOk=isValidPairingToken(pairing);if(!apiKeyOk&&!pairingOk)return set.status=401,{success:!1,state:stateForAuth,error:"Unauthorized: provide x-agent-api-key OR x-pairing-token for bootstrap"}}let{clientId,clientSecret,workspaceId,globalGatewayUrl,workspaceGatewayUrl}=body;if(!clientId||!clientSecret||!workspaceId||!globalGatewayUrl||!workspaceGatewayUrl)return set.status=400,{success:!1,state:getDaemonProfile().getBootState(),error:"clientId, clientSecret, workspaceId, globalGatewayUrl, and workspaceGatewayUrl are required"};let safeGlobalGatewayUrl,safeWorkspaceGatewayUrl;try{safeGlobalGatewayUrl=assertAllowedGatewayUrl(globalGatewayUrl,"globalGatewayUrl"),safeWorkspaceGatewayUrl=assertAllowedGatewayUrl(workspaceGatewayUrl,"workspaceGatewayUrl")}catch(err){return set.status=400,{success:!1,state:getDaemonProfile().getBootState(),error:err instanceof Error?err.message:String(err)}}{let ctx=currentProfileOrNull()??getDaemonProfile();try{await writeAgentConfigAwaitSecrets({clientId,clientSecret,workspaceId,organizationId:body.organizationId,globalGatewayUrl:safeGlobalGatewayUrl,workspaceGatewayUrl:safeWorkspaceGatewayUrl,agentRecordId:body.agentRecordId,scopes:body.scopes},{dir:ctx.dataDir,scope:ctx.name})}catch(err){return set.status=500,{success:!1,state:getDaemonProfile().getBootState(),error:`Failed to persist gateway credentials to secrets backend: ${err instanceof Error?err.message:String(err)}`}}}let state=getDaemonProfile().getBootState();if(state==="ready"){let db=getDb();if(db){if(await db.setConfig("gateway-auth:clientId",clientId),await db.setConfig("gateway-auth:clientSecret",clientSecret),await db.setConfig("gateway-auth:workspaceId",workspaceId),body.organizationId)await db.setConfig("gateway-auth:organizationId",body.organizationId);if(await db.setConfig("gateway-auth:globalGatewayUrl",safeGlobalGatewayUrl),await db.setConfig("gateway-auth:workspaceGatewayUrl",safeWorkspaceGatewayUrl),body.agentRecordId)await db.setConfig("gateway-auth:agentRecordId",body.agentRecordId)}return gatewayClient.configure({globalGatewayUrl:safeGlobalGatewayUrl,workspaceGatewayUrl:safeWorkspaceGatewayUrl,clientId,clientSecret,workspaceId,organizationId:body.organizationId}),getDaemonProfile().logger.info("preconfig","Gateway credentials re-configured (agent was already ready)"),{success:!0,state,message:"Gateway auth re-configured"}}return getDaemonProfile().logger.info("preconfig","Gateway credentials received \u2014 beginning finalize (background)"),invalidatePairingToken(),runFinalize({clientId,clientSecret,workspaceId,organizationId:body.organizationId,globalGatewayUrl:safeGlobalGatewayUrl,workspaceGatewayUrl:safeWorkspaceGatewayUrl},{agentRecordId:body.agentRecordId,scopes:body.scopes}).catch((err)=>{getDaemonProfile().logger.error("preconfig","Background finalize threw",{error:err instanceof Error?err.message:String(err)})}),set.status=202,{success:!0,state:getDaemonProfile().getBootState(),agentRecordId:body.agentRecordId,message:"Gateway credentials accepted; finalize is running in the background. Poll /health/ready for completion."}},{body:t.Object({clientId:t.Optional(t.String()),clientSecret:t.Optional(t.String()),workspaceId:t.Optional(t.String()),organizationId:t.Optional(t.String()),globalGatewayUrl:t.Optional(t.String()),workspaceGatewayUrl:t.Optional(t.String()),scopes:t.Optional(t.Array(t.String())),agentRecordId:t.Optional(t.String())})})}import{existsSync as existsSync2,mkdirSync as mkdirSync2,rmSync,readdirSync}from"fs";import{join as join3}from"path";function profileDir(name){return join3(getVibecontrolsProductDir(),"agents",validateAgentName(name))}function listProfileNames(){let dir=join3(getVibecontrolsProductDir(),"agents");if(!existsSync2(dir))return[];return readdirSync(dir,{withFileTypes:!0}).filter((entry)=>entry.isDirectory()).map((entry)=>entry.name).filter((name)=>/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(name))}function profileDataDir(name){return join3(getVibecontrolsProductDir(),"agents",validateAgentName(name))}function readConfigForProfile(name){return readAgentConfig({dir:profileDataDir(name)})}function writeConfigForProfile(name,cfg){return writeAgentConfig(cfg,{dir:profileDataDir(name),scope:validateAgentName(name)})}function summarize(name){let defaultName=getDefaultProfile(),instance=getInstances().find((p)=>p.name===name),ctx=profileRegistry.get(name),summary={name,isDefault:name===defaultName,isRunning:instance?.status==="running",attached:ctx!==void 0,bootState:ctx?ctx.getBootState():null,dataDir:ctx?ctx.dataDir:null};if(instance)summary.port=instance.port,summary.pid=instance.pid,summary.startTime=instance.startTime;if(existsSync2(profileDir(name)))summary.config=redactUnknownSecrets(readConfigForProfile(name));return summary}function createProfileManagementRoutes(deps={}){let sm=deps.serviceManagerFactory?deps.serviceManagerFactory():new ServiceManager;return new Elysia({prefix:"/api/profiles"}).get("/",()=>{let onDisk=listProfileNames(),defaultName=getDefaultProfile(),names=new Set(onDisk);return names.add(defaultName),[...names].sort().map((n)=>summarize(n))}).post("/",async({body,set})=>{try{let name=validateAgentName(body.name),dir=profileDir(name);if(existsSync2(dir))return set.status=409,{error:"exists",message:`Profile '${name}' already exists.`};let seed;if(body.copyFrom){let src=validateAgentName(body.copyFrom);if(!existsSync2(profileDir(src)))return set.status=400,{error:"copy-source-missing",message:`Profile '${src}' does not exist.`};seed=readConfigForProfile(src),delete seed["static-api-key"],delete seed.agentRecordId,delete seed.apiKeys}else seed=getAgentConfigSeed();if(body.gatewayUrl)seed.globalGatewayUrl=body.gatewayUrl;if(body.workspaceGatewayUrl)seed.workspaceGatewayUrl=body.workspaceGatewayUrl;if(body.clientId)seed.clientId=body.clientId;if(body.clientSecret)seed.clientSecret=body.clientSecret;if(body.workspaceId)seed.workspaceId=body.workspaceId;if(body.organizationId)seed.organizationId=body.organizationId;mkdirSync2(dir,{recursive:!0}),writeConfigForProfile(name,seed);try{getOrCreateProfile(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process attach failed for '${name}'`,{error:String(err)})}if(getDaemonProfile().logger.info("profile-mgmt",`Created profile '${name}' at ${dir}`),body.start){let startCfg={name,port:body.port??0,daemon:!0,dbPath:getProfileDataDir(),profile:name};await sm.startDaemon(startCfg)}return set.status=201,summarize(name)}catch(err){return set.status=400,{error:"create-failed",message:err instanceof Error?err.message:String(err)}}},{body:t.Object({name:t.String(),gatewayUrl:t.Optional(t.String()),workspaceGatewayUrl:t.Optional(t.String()),clientId:t.Optional(t.String()),clientSecret:t.Optional(t.String()),workspaceId:t.Optional(t.String()),organizationId:t.Optional(t.String()),copyFrom:t.Optional(t.String()),start:t.Optional(t.Boolean()),port:t.Optional(t.Number())})}).get("/:name",({params,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};return summarize(name)}catch(err){return set.status=400,{error:"bad-request",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()})}).put("/:name",({params,body,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};let forbidden=["static-api-key","apiKeys","agentRecordId"];for(let key of forbidden)if(key in body)return set.status=400,{error:"forbidden-field",message:`Field '${key}' is managed via dedicated commands (vibe key rotate / vibe profile create). It cannot be patched here.`};let next={...readConfigForProfile(name),...body};return writeConfigForProfile(name,next),summarize(name)}catch(err){return set.status=400,{error:"update-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()}),body:t.Record(t.String(),t.Any())}).delete("/:name",async({params,body,query,set})=>{try{let name=validateAgentName(params.name);if(name===getDefaultProfile())return set.status=409,{error:"is-default",message:`Cannot delete active default profile '${name}'. Switch to another profile first.`};let purge=query?.purge==="true"||query?.purge==="1",hasDir=existsSync2(profileDir(name)),inRegistry=profileRegistry.has(name);if(!hasDir&&!inRegistry)return set.status=404,{error:"not-found",message:`No profile '${name}'.`};if(hasDir&&!purge&&!body?.confirm)return set.status=400,{error:"confirm-required",message:"Pass { confirm: true } in the body or `?purge=true` in the query to delete the on-disk dataDir (destructive)."};try{await sm.stop(name)}catch{}try{await profileRegistry.destroy(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process detach failed for '${name}'`,{error:String(err)})}if(hasDir)if(body?.keepConfig)for(let entry of readdirSync(profileDir(name))){if(entry==="config.json")continue;rmSync(join3(profileDir(name),entry),{recursive:!0,force:!0})}else rmSync(profileDir(name),{recursive:!0,force:!0});return getDaemonProfile().logger.info("profile-mgmt",`Deleted profile '${name}'`),{ok:!0,deleted:name,keptConfig:!!body?.keepConfig,detached:inRegistry,purged:hasDir}}catch(err){return set.status=400,{error:"delete-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()}),query:t.Optional(t.Object({purge:t.Optional(t.String())})),body:t.Optional(t.Object({confirm:t.Optional(t.Boolean()),keepConfig:t.Optional(t.Boolean())}))}).post("/:name/switch",({params,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};return setDefaultProfile(name),getDaemonProfile().logger.info("profile-mgmt",`Default profile switched to '${name}'`),{ok:!0,defaultProfile:name}}catch(err){return set.status=400,{error:"switch-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()})}).post("/:name/start",async({params,body,set})=>{try{let name=validateAgentName(params.name);if(!existsSync2(profileDir(name)))return set.status=404,{error:"not-found",message:`No profile '${name}'.`};try{getOrCreateProfile(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process attach failed for '${name}'`,{error:String(err)})}if(!getInstances().some((p)=>p.name===name&&p.status==="running")){let startCfg={name,port:body?.port??0,daemon:!0,dbPath:getProfileDataDir(),profile:name};await sm.startDaemon(startCfg)}return summarize(name)}catch(err){return set.status=500,{error:"start-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()}),body:t.Optional(t.Object({port:t.Optional(t.Number())}))}).post("/:name/stop",async({params,set})=>{try{let name=validateAgentName(params.name);try{await sm.stop(name)}catch{}try{await profileRegistry.destroy(name)}catch(err){getDaemonProfile().logger.warn("profile-mgmt",`In-process detach failed for '${name}'`,{error:String(err)})}return summarize(name)}catch(err){return set.status=500,{error:"stop-failed",message:err instanceof Error?err.message:String(err)}}},{params:t.Object({name:t.String()})})}function buildProfileStats(){let rss=process.memoryUsage(),ru=process.resourceUsage(),totalCpuUserMs=ru.userCPUTime/1000,totalCpuSystemMs=ru.systemCPUTime/1000,totalCpuMs=totalCpuUserMs+totalCpuSystemMs,profiles=profileRegistry.list().map((ctx)=>{let childPidCount;try{childPidCount=listTrackedByOwner(ctx.spawnOwnerKey).length}catch{childPidCount=0}return{name:ctx.name,status:ctx.getBootState(),childPidCount,cpuMs:totalCpuMs,heapBytes:rss.heapUsed,dataDir:ctx.dataDir}});return{process:{pid:process.pid,uptimeSeconds:process.uptime(),cpuUserMs:totalCpuUserMs,cpuSystemMs:totalCpuSystemMs,heapUsedBytes:rss.heapUsed,heapTotalBytes:rss.heapTotal,rssBytes:rss.rss},profiles,notes:{perProfileCpu:"deferred",perProfileHeap:"deferred"}}}function createProfileStatsRoutes(){return new Elysia({prefix:"/api/profile-stats"}).get("/",()=>buildProfileStats())}function withTimeout(p,ms){return Promise.race([p,new Promise((_,reject)=>setTimeout(()=>reject(Error(`timeout after ${ms}ms`)),ms))])}function isEnoent(err){let msg=err instanceof Error?err.message:String(err);return/ENOENT/.test(msg)||/no such file or directory/i.test(msg)}async function runDbProbe(db){let probeKey=`__health__:probe:${typeof crypto<"u"&&typeof crypto.randomUUID==="function"?crypto.randomUUID():`${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`}`,value=String(Date.now());if(await withTimeout(db.setConfig(probeKey,value),2000),await withTimeout(db.getConfig(probeKey),2000)!==value)throw Error("round-trip mismatch");try{await withTimeout(db.deleteConfig(probeKey),500)}catch{}}async function checkDb(db,dbPath){if(!db)return{ok:!1,message:"database not opened"};let start=performance.now();try{return await runDbProbe(db),{ok:!0,durationMs:performance.now()-start}}catch(err){if(dbPath&&isEnoent(err))try{return(await import("fs")).mkdirSync(dbPath,{recursive:!0}),await runDbProbe(db),{ok:!0,message:`recovered: dbPath ${dbPath} was missing \u2014 recreated`,durationMs:performance.now()-start}}catch(retryErr){return{ok:!1,message:`ENOENT on dbPath ${dbPath}: ${retryErr instanceof Error?retryErr.message:String(retryErr)}`,durationMs:performance.now()-start}}return{ok:!1,message:err instanceof Error?err.message:String(err),durationMs:performance.now()-start}}}function checkProvider(registry,type){if(!registry)return{ok:!1,message:"no service registry"};let awaiting=getDaemonProfile().getBootState()==="awaiting-config";if(type==="tunnel"){let p=registry.getProvider("tunnel");if(!p&&process.env.AGENT_TUNNEL==="false")return{ok:!0,message:"tunnel disabled via AGENT_TUNNEL=false"};if(p)return{ok:!0,message:`provider: ${p.name}`};return{ok:!1,message:awaiting?"agent is awaiting-config \u2014 POST OAuth credentials to /api/agent/gateway-auth to register tunnel provider":"no tunnel provider registered"}}if(type==="session"){let p=registry.getProvider("session");if(p)return{ok:!0,message:`provider: ${p.name}`};return{ok:!1,message:awaiting?"agent is awaiting-config \u2014 POST OAuth credentials to /api/agent/gateway-auth to register session provider":"no session provider registered"}}return{ok:!0}}async function checkPluginHealth(pm){if(!pm)return{};let results={};for(let plugin of pm.getAllPlugins()){let fn=plugin.getHealth;if(typeof fn!=="function")continue;let start=performance.now();try{let out=await withTimeout(Promise.resolve(fn.call(plugin)),2000);results[plugin.name]={ok:out?.ok!==!1,message:out?.message,durationMs:performance.now()-start}}catch(err){results[plugin.name]={ok:!1,message:err instanceof Error?err.message:String(err),durationMs:performance.now()-start}}}return results}function createHealthRoutes(depsOrRegistry){let deps=depsOrRegistry&&"serviceRegistry"in depsOrRegistry?depsOrRegistry:{serviceRegistry:depsOrRegistry};return new Elysia({prefix:"/health"}).get("/",()=>{return{status:"ok",lifecycleState:deps.serviceRegistry?.getService("agent","lifecycle")?.getState()??"running",bootState:getDaemonProfile().getBootState(),timestamp:new Date().toISOString(),uptime:process.uptime()}}).get("/live",()=>({status:"ok"})).get("/ready",async({set})=>{let lifecycleState=deps.serviceRegistry?.getService("agent","lifecycle")?.getState()??"running",bootState=getDaemonProfile().getBootState(),components={};components.boot={ok:bootState==="ready",message:bootState==="ready"?void 0:`boot state ${bootState}`},components.lifecycle={ok:lifecycleState==="running",message:lifecycleState==="running"?void 0:`lifecycle ${lifecycleState}`},components.db=await checkDb(deps.getDb?.()??null,deps.getDbPath?.()??null),components.tunnel=checkProvider(deps.serviceRegistry,"tunnel"),components.session=checkProvider(deps.serviceRegistry,"session");let plugins=await checkPluginHealth(deps.getPluginManager?.()??null),allOk=Object.values(components).every((r)=>r.ok)&&Object.values(plugins).every((r)=>r.ok);if(!allOk)set.status=503;let reasons=bootState==="degraded"?getDaemonProfile().getDegradedReasons():[],daemonName=getDaemonProfile().name,profilesMap={};for(let ctx of profileRegistry.list())profilesMap[ctx.name]={bootState:ctx.getBootState(),ok:ctx.getBootState()==="ready",degradedReasons:ctx.getDegradedReasons()};let lastFinalizeError=getDaemonProfile().getLastFinalizeError(),body={status:allOk?"ok":"degraded",lifecycleState,bootState,daemon:{profile:daemonName,bootState,ok:bootState==="ready"},profiles:profilesMap,components,plugins,degradedReasons:reasons,lastFinalizeError,timestamp:new Date().toISOString()};if(!allOk)getDaemonProfile().logger.warn("health","Readiness probe failed",{components,plugins,bootState});return body})}var inflight=0,totalServed=0;function createInflightTracker(){return new Elysia({name:"inflight-tracker"}).onRequest(()=>{inflight+=1}).onAfterResponse(()=>{inflight=Math.max(0,inflight-1),totalServed+=1})}function createStatsRoutes(){return new Elysia({prefix:"/api/stats"}).get("/inflight",()=>({inflight:Math.max(0,inflight-1),totalServed}))}var ALLOWED_COMMANDS=Object.freeze(["code","code-insiders","codium","cursor","idea","pycharm","webstorm","goland","phpstorm","rubymine","clion","rider","zed","subl","vim","nvim","emacs","neovide","fleet","helix"]),ALLOWED_SET=new Set(ALLOWED_COMMANDS),ALLOWED_ARG_FLAGS=new Set(["--new-window","-n","--reuse-window","-r","--wait","-w","--add","--goto"]);function which(cmd){try{return Bun.which(cmd)??null}catch{return null}}function createEditorRoutes(){return new Elysia({prefix:"/api/editor"}).get("/allowlist",()=>({commands:[...ALLOWED_COMMANDS]})).get("/detect",async()=>{let results=ALLOWED_COMMANDS.map((cmd)=>[cmd,which(cmd)]),available=[],paths={};for(let[cmd,p]of results)if(p)available.push(cmd),paths[cmd]=p;return{available,paths}}).post("/open",async({body,set})=>{let{command,path:rawPath,args=[]}=body;if(!ALLOWED_SET.has(command))return set.status=400,{ok:!1,error:`Command '${command}' is not in the IDE allowlist`};for(let a of args)if(typeof a!=="string"||!ALLOWED_ARG_FLAGS.has(a))return set.status=400,{ok:!1,error:`Argument '${a}' is not in the flag allowlist`};let resolvedPath;try{resolvedPath=(await resolveSafePath(rawPath,{mustExist:!0})).realPath}catch(err){return set.status=400,{ok:!1,error:`Invalid path: ${err instanceof Error?err.message:String(err)}`}}let resolvedCmd=which(command);if(!resolvedCmd)return set.status=404,{ok:!1,error:`'${command}' is not installed or not on PATH on this agent`};try{let child=spawnTracked({owner:`editor:${command}`,cmd:[resolvedCmd,...args,resolvedPath],env:process.env,stdout:"ignore",stderr:"ignore",stdin:"ignore",meta:{command,path:resolvedPath}});return getDaemonProfile().logger.info("editor",`Launched ${command} on ${resolvedPath}`,{pid:child.pid}),getDaemonProfile().audit.emit("agent","editor.open",{command,path:resolvedPath,pid:child.pid}),{ok:!0,command,resolvedPath,pid:child.pid}}catch(err){return set.status=500,{ok:!1,error:`Failed to spawn editor: ${err instanceof Error?err.message:String(err)}`}}},{body:t.Object({command:t.String({minLength:1,maxLength:40}),path:t.String({minLength:1,maxLength:4096}),args:t.Optional(t.Array(t.String({maxLength:32})))})})}var REGISTRY_KEY=Symbol.for("@vibecontrols/plugin-sdk:contextProviders@1");function getRegistry(){let slots=globalThis,existing=slots[REGISTRY_KEY];if(existing)return existing;let created=new Map;return slots[REGISTRY_KEY]=created,created}function listContextProviders(){return Array.from(getRegistry().values())}function safeHost(maybeUrl){if(!maybeUrl)return null;try{return new URL(maybeUrl).host}catch{return null}}async function buildAgentSelfContext(deps,args){let pm=deps.getPluginManager(),loadedPlugins=pm?pm.getPluginDetails().map((p)=>({pluginName:p.pluginName,packageName:p.packageName,version:p.version,tags:p.tags??[]})):[],mem=process.memoryUsage(),tunnelHost=safeHost(deps.getTunnelHost?.()??null);return{pluginName:"agent",description:"vibecontrols-agent runtime self-report",generatedAt:new Date().toISOString(),data:{profile:args.profile,vibeId:args.vibeId??null,runtime:{bunVersion:typeof Bun<"u"?Bun.version:null,nodeVersion:process.versions.node??null,platform:process.platform,arch:process.arch,pid:process.pid,uptimeSeconds:Math.round(process.uptime()),memory:{rssMB:Math.round(mem.rss/1048576),heapUsedMB:Math.round(mem.heapUsed/1048576)}},agent:{version:deps.getAgentVersion?.()??null,profileRegistered:getVibecontrolsProfile(),tunnelHost},plugins:{count:loadedPlugins.length,items:loadedPlugins}}}}var DEFAULT_PROVIDER_TIMEOUT_MS=2000,SINGLE_FLIGHT_TTL_MS=1000;async function withTimeout2(promise,timeoutMs){let timer,timeout=new Promise((_,reject)=>{timer=setTimeout(()=>reject(Error(`timeout after ${timeoutMs}ms`)),timeoutMs)});try{return await Promise.race([promise,timeout])}finally{if(timer)clearTimeout(timer)}}async function runOne(provider,args){let start=performance.now(),timeoutMs=provider.timeoutMs??DEFAULT_PROVIDER_TIMEOUT_MS;try{let data=await withTimeout2(provider.getContext(args),timeoutMs);if(!data.generatedAt)data.generatedAt=new Date().toISOString();return{ok:!0,data,durationMs:Math.round(performance.now()-start)}}catch(err){return{ok:!1,error:err.message??String(err),durationMs:Math.round(performance.now()-start)}}}class ContextAggregator{deps;inflight=new Map;constructor(deps){this.deps=deps}async aggregate(args){let key=`${args.profile}::${args.vibeId??""}`,existing=this.inflight.get(key);if(existing&&Date.now()-existing.ts<SINGLE_FLIGHT_TTL_MS)return existing.promise;let promise=this.doAggregate(args).finally(()=>{setTimeout(()=>{let entry=this.inflight.get(key);if(entry&&entry.promise===promise)this.inflight.delete(key)},SINGLE_FLIGHT_TTL_MS).unref?.()});return this.inflight.set(key,{ts:Date.now(),promise}),promise}async selfContext(args){return buildAgentSelfContext(this.deps,args)}async pluginContext(name,args){let provider=listContextProviders().find((p)=>p.name===name);if(!provider)return null;return runOne(provider,args)}async pluginsContext(args){let providers=listContextProviders(),settled=await Promise.allSettled(providers.map((p)=>runOne(p,args))),out={};return providers.forEach((p,i)=>{let r=settled[i];if(r&&r.status==="fulfilled")out[p.name]=r.value;else if(r&&r.status==="rejected")out[p.name]={ok:!1,error:r.reason instanceof Error?r.reason.message:String(r.reason),durationMs:0}}),out}async doAggregate(args){let[agent,plugins]=await Promise.all([this.selfContext(args),this.pluginsContext(args)]);return{agent,plugins}}}function parseVibeId(query){let raw=query.vibeId;if(typeof raw!=="string")return;let v=raw.trim();return v.length>0&&v.length<=128?v:void 0}function createContextRoutes(deps){let aggregator=new ContextAggregator(deps);return new Elysia({prefix:"/api/agent/context"}).get("/",async({params,query})=>{let profile=params?.profile??"default",vibeId=parseVibeId(query);return aggregator.aggregate({profile,vibeId})},{query:t.Object({vibeId:t.Optional(t.String())})}).get("/agent",async({params,query})=>{let profile=params?.profile??"default",vibeId=parseVibeId(query);return aggregator.selfContext({profile,vibeId})},{query:t.Object({vibeId:t.Optional(t.String())})}).get("/plugins",async({params,query})=>{let profile=params?.profile??"default",vibeId=parseVibeId(query);return aggregator.pluginsContext({profile,vibeId})},{query:t.Object({vibeId:t.Optional(t.String())})}).get("/plugins/:name",async({params,query,set})=>{let profile=params?.profile??"default",name=params?.name;if(!name)return set.status=400,{error:"missing plugin name"};let vibeId=parseVibeId(query),entry=await aggregator.pluginContext(name,{profile,vibeId});if(!entry)return set.status=404,{error:`no context provider registered under name "${name}"`};return entry},{params:t.Object({name:t.String()}),query:t.Object({vibeId:t.Optional(t.String())})})}var FINALIZE_EXEMPT_PREFIXES=["/health","/api/agent/status","/api/agent/version","/api/agent/identity","/api/agent/api-key","/api/agent/tunnel","/api/agent/system","/api/agent/gateway-auth","/api/stats","/metrics","/api/profiles","/api/profile-stats"];function isFinalizeExempt(path){return FINALIZE_EXEMPT_PREFIXES.some((p)=>path.startsWith(p))}var UUID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function normalizeRouteLabel(path){return path.split("/").map((seg)=>{if(!seg)return seg;if(/^\d+$/.test(seg))return":id";if(UUID_RE.test(seg))return":id";if(seg.length>=24&&/^[A-Za-z0-9_-]+$/.test(seg))return":id";return seg}).join("/")}async function createApp(options){let{port,host,dbPath,logLevel,corsOrigin}=options,effectiveDbPath=dbPath??getProfileDataDir();try{(await import("fs")).mkdirSync(effectiveDbPath,{recursive:!0})}catch(err){throw getDaemonProfile().logger.error("app","Failed to create dbPath dir at boot",{dbPath:effectiveDbPath,error:String(err)}),Error(`Cannot create dbPath ${effectiveDbPath}: ${err instanceof Error?err.message:String(err)}`,{cause:err})}if(logLevel)getDaemonProfile().logger.setLevel(logLevel);(async()=>{let{logShipper}=await import("./log-shipper-9rtt4y1s.js");logShipper.init()})(),(async()=>{let version=await getPackageVersion();await getMetricsRegistry({version,profile:getVibecontrolsProfile()})})();try{let{checkDependencies}=await import("./bootstrap.service-fknk5beq.js"),deps=checkDependencies(),missingRequired=deps.filter((d)=>d.required&&!d.installed),missingOptional=deps.filter((d)=>!d.required&&!d.installed);if(missingRequired.length>0)getDaemonProfile().logger.error("app",`Missing required system deps: ${missingRequired.map((d)=>d.name).join(", ")}. Run \`vibe setup\` to install.`);if(missingOptional.length>0)getDaemonProfile().logger.warn("app",`Missing optional system deps: ${missingOptional.map((d)=>d.name).join(", ")}. Some plugins may fail their prereqs check.`)}catch(err){getDaemonProfile().logger.warn("app","Boot-time dependency check failed",{error:String(err)})}try{getDaemonProfile().audit.runRetention()}catch(err){getDaemonProfile().logger.warn("app","Audit-log retention sweep failed",{error:String(err)})}let serviceRegistry=new ServiceRegistry;startBootstrapTunnel(serviceRegistry,{port});let CORS_METHODS="GET, POST, PUT, PATCH, DELETE, OPTIONS",CORS_HEADERS="Content-Type, x-agent-api-key, Authorization, x-requested-with",configuredCorsOrigins=Array.isArray(corsOrigin)?corsOrigin:(corsOrigin??"").split(",").map((origin)=>origin.trim()).filter(Boolean),trustedProductionOrigins=new Set(["https://vibecontrols.com","https://app.vibecontrols.com","https://alphaapp.vibecontrols.com","https://burdenoff.com","https://app.burdenoff.com","https://alphaapp.burdenoff.com"]),isAllowedOrigin=(origin)=>{try{let url=new URL(origin);if(configuredCorsOrigins.includes(origin))return!0;if(configuredCorsOrigins.includes("*"))return!1;return url.hostname==="localhost"||url.hostname==="127.0.0.1"||url.hostname==="0.0.0.0"||url.hostname.endsWith(".local.burdenoff.com")||trustedProductionOrigins.has(url.origin)}catch{return!1}},db=null,pluginManager=null,hostServices=null,lifecycle=null,pluginPublicPaths=()=>[],pluginRoutesApp=null,uiServerApp=null,finalizeInFlight=null,SHUTDOWN_PHASE_MS=5000,runPhase=async(name,fn)=>{let started=Date.now();try{await Promise.race([Promise.resolve(fn()),new Promise((_,reject)=>setTimeout(()=>reject(Error(`shutdown phase ${name} exceeded ${SHUTDOWN_PHASE_MS}ms`)),SHUTDOWN_PHASE_MS))]),getDaemonProfile().logger.info("app",`shutdown.phase ${name} complete`,{ms:Date.now()-started})}catch(err){getDaemonProfile().logger.warn("app",`shutdown.phase ${name} failed`,{error:String(err),ms:Date.now()-started})}},stopFn=async(context)=>{if(getDaemonProfile().logger.info("app","Agent shutdown starting",{reason:context?.reason??"shutdown"}),await runPhase("plugins",async()=>{if(pluginManager)await pluginManager.dispatchServerStop(context)}),await runPhase("bootstrap-tunnel",()=>stopBootstrapTunnel()),await runPhase("tracked-subprocesses",async()=>{let{killAllTracked}=await import("./subprocess-vks78xmn.js");await killAllTracked()}),context?.reason!=="reload")await runPhase("db-close",async()=>{if(db)await db.close()});getDaemonProfile().logger.close(),getDaemonProfile().logger.info("app","Agent server stopped")},finalize=async(creds,opts)=>{if(getDaemonProfile().getBootState()==="ready")return{ok:!0};if(finalizeInFlight)return finalizeInFlight;return finalizeInFlight=(async()=>{getDaemonProfile().setBootState("initializing");try{let safeCreds={...creds,globalGatewayUrl:assertAllowedGatewayUrl(creds.globalGatewayUrl,"globalGatewayUrl"),workspaceGatewayUrl:assertAllowedGatewayUrl(creds.workspaceGatewayUrl,"workspaceGatewayUrl")},encryptionKey=await fetchEncryptionKey(safeCreds),resolvedDbPath=effectiveDbPath,cfgForStorage=readAgentConfig({dir:getDaemonProfile().dataDir}),storageAdapterName=cfgForStorage.storageAdapter??process.env.VIBE_STORAGE_ADAPTER??void 0,storageAdapterOptions=cfgForStorage.storageOptions;try{db=await createAgentDatabase({dbPath:resolvedDbPath,encryptionKey,adapterName:storageAdapterName,adapterOptions:storageAdapterOptions})}catch(openErr){let message=openErr instanceof Error?openErr.message:String(openErr);getDaemonProfile().logger.error("app","Storage open failed; quarantining and retrying once",{error:message,path:resolvedDbPath});try{let fs=await import("fs");if(fs.existsSync(resolvedDbPath)){let ts=new Date().toISOString().replace(/[:.]/g,"-"),broken=`${resolvedDbPath}.broken-${ts}`;fs.renameSync(resolvedDbPath,broken),getDaemonProfile().logger.warn("app",`Quarantined storage to ${broken} \u2014 fresh DB will be created`)}db=await createAgentDatabase({dbPath:resolvedDbPath,encryptionKey,adapterName:storageAdapterName,adapterOptions:storageAdapterOptions})}catch(retryErr){let retryMsg=retryErr instanceof Error?retryErr.message:String(retryErr);throw getDaemonProfile().recordDegradedReason("storage",retryMsg),Error(`Storage open failed twice; agent is degraded: ${retryMsg}`,{cause:retryErr})}}if(await writeAgentConfigAwaitSecrets({clientId:safeCreds.clientId,clientSecret:safeCreds.clientSecret,workspaceId:safeCreds.workspaceId,organizationId:safeCreds.organizationId,globalGatewayUrl:safeCreds.globalGatewayUrl,workspaceGatewayUrl:safeCreds.workspaceGatewayUrl,agentRecordId:opts?.agentRecordId,scopes:opts?.scopes},{dir:getDaemonProfile().dataDir,scope:getDaemonProfile().name}),await db.setConfig("gateway-auth:clientId",safeCreds.clientId),await db.setConfig("gateway-auth:clientSecret",safeCreds.clientSecret),await db.setConfig("gateway-auth:globalGatewayUrl",safeCreds.globalGatewayUrl),await db.setConfig("gateway-auth:workspaceGatewayUrl",safeCreds.workspaceGatewayUrl),await db.setConfig("gateway-auth:workspaceId",safeCreds.workspaceId),safeCreds.organizationId)await db.setConfig("gateway-auth:organizationId",safeCreds.organizationId);if(opts?.agentRecordId)await db.setConfig("gateway-auth:agentRecordId",opts.agentRecordId);serviceRegistry.db=db,await serviceRegistry.hydrateDefaultsFromDb(),pluginManager=new PluginManager(db),await pluginManager.loadCorePlugins();try{await pluginManager.loadAll()}catch(err){getDaemonProfile().logger.warn("app","Failed to load some external plugins",{error:String(err)})}let autoInstallDisabled=process.env.VIBE_SKIP_AUTO_INSTALL==="1"||await db.getConfig("plugins:auto-install-disabled");if(autoInstallDisabled!=="true"&&autoInstallDisabled!==!0)try{let installed=await pluginManager.ensureDefaultPlugins((message)=>getDaemonProfile().logger.info("app",`[auto-install] ${message}`));if(installed.length>0)getDaemonProfile().logger.info("app",`Auto-installed ${installed.length} default plugin(s): ${installed.map((p)=>p.packageName).join(", ")}`)}catch(err){getDaemonProfile().logger.warn("app","Failed to auto-install default plugins",{error:String(err)})}getDaemonProfile().logger.info("app",`Plugins loaded: ${pluginManager.getPluginDetails().length} total (${pluginManager.getPluginDetails().filter((p)=>p.isCore).length} core)`);try{let details=pluginManager.getPluginDetails(),byStatus={loaded:details.filter((p)=>p.loaded).length,failed:details.filter((p)=>!p.loaded).length};setPluginsLoaded(byStatus)}catch{}let storageProvider={async get(namespace,key){return await db.getPluginState(namespace,key)??null},async set(namespace,key,value){await db.setPluginState(namespace,key,value)},async delete(namespace,key){return db.deletePluginState(namespace,key)},async list(namespace){return await db.getAllPluginState(namespace)},async deleteAll(namespace){return db.deleteAllPluginState(namespace)}},packageVersion=await getPackageVersion();hostServices={storage:storageProvider,logger:getDaemonProfile().logger,serviceRegistry,getProvider:(type)=>serviceRegistry.getProvider(type),getAgentBaseUrl:()=>process.env.AGENT_URL||`http://localhost:${port}`,getAgentVersion:()=>packageVersion,validateApiKey:(key)=>isValidAgentApiKey(key),setCodeServerSessionValidator:(fn)=>{codeServerSessionValidator=fn},broadcast,workspaceQuery:async(query,variables)=>gatewayClient.workspaceQuery(query,variables),isGatewayConfigured:()=>gatewayClient.isConfigured(),getAgentRecordId:async()=>await db.getConfig("gateway-auth:agentRecordId")??null,getWorkspaceId:async()=>await db.getConfig("gateway-auth:workspaceId")??null,getConfig:async(key)=>await db.getConfig(key)??void 0,getPluginRegistry:()=>getPluginRegistry(),getDataDir:()=>getVibecontrolsDir(),cliContributors:new CliContributorRegistry,audit:{emit:(event,payload)=>getDaemonProfile().audit.emit("agent",event,payload??{})},telemetry:{emit:(event,payload)=>{import("./telemetry-rew0mtj2.js").then(({telemetryService})=>telemetryService.emit(event,payload??{})).catch(()=>{})}},os:getOsAdapter(),iframeBridge:createIframeBridge()},gatewayClient.configure({globalGatewayUrl:safeCreds.globalGatewayUrl,workspaceGatewayUrl:safeCreds.workspaceGatewayUrl,clientId:safeCreds.clientId,clientSecret:safeCreds.clientSecret,workspaceId:safeCreds.workspaceId,organizationId:safeCreds.organizationId});let routeDeps={db,serviceRegistry,pluginManager,broadcast,hostServices};routeDeps.app=app;let pluginRoutesAppInitialized=!1,rebuildPluginSurfaces=()=>{let{routes:pluginRoutes,getPublicPaths,mountPlugin}=createPluginRouter(pluginManager,routeDeps);if(routeDeps.mountPlugin=mountPlugin,pluginPublicPaths=getPublicPaths,markPluginPublicPathsDirty(),pluginRoutesApp=pluginRoutes,uiServerApp=createUIServer(pluginManager),pluginManager&&pluginRoutesAppInitialized&&hostServices)pluginManager.dispatchServerStart(pluginRoutesApp,hostServices).catch((err)=>getDaemonProfile().logger.warn("app","Re-dispatch of onServerStart after plugin reload failed",{error:String(err)}));pluginRoutesAppInitialized=!0};routeDeps.rebuildPluginSurfaces=rebuildPluginSurfaces,rebuildPluginSurfaces(),lifecycle=new LifecycleManager(serviceRegistry,pluginManager,db,app,hostServices,stopFn),serviceRegistry.registerService("agent","lifecycle",lifecycle),getDaemonProfile().attachServices({db,serviceRegistry,pluginManager,lifecycle,hostServices}),mountProfileRoutes(getDaemonProfile(),{rebuildPluginSurfaces,mountPlugin:routeDeps.mountPlugin});try{await getDaemonProfile().keyVault.bindApiKey(getAgentApiKey())}catch(err){getDaemonProfile().logger.warn("app","Failed to bind api key into daemon keyVault",{error:String(err)})}let criticalFailures=[];try{let{failures}=await pluginManager.dispatchServerStart(pluginRoutesApp,hostServices);for(let f of failures)if(isCriticalPlugin(f.plugin)||(f.packageName?isCriticalPlugin(f.packageName):!1))criticalFailures.push(f),getDaemonProfile().recordDegradedReason(f.plugin,f.error);else getDaemonProfile().logger.warn("app",`Non-critical plugin ${f.plugin} failed onServerStart; continuing`,{error:f.error})}catch(err){getDaemonProfile().logger.warn("app","Error during plugin onServerStart dispatch",{error:String(err)})}try{await pluginManager.dispatchServerReady(app,hostServices)}catch(err){getDaemonProfile().logger.warn("app","Error during plugin onServerReady dispatch",{error:String(err)})}if(getDaemonProfile().clearFinalizeError(),criticalFailures.length>0)getDaemonProfile().setBootState("degraded"),getDaemonProfile().logger.error("app","Agent finalized but degraded \u2014 critical plugins failed",{failures:criticalFailures.map((f)=>`${f.plugin}: ${f.error}`)});else getDaemonProfile().setBootState("ready"),getDaemonProfile().logger.info("app","Agent finalized \u2014 state=ready"),getDaemonProfile().audit.emit("agent","gateway-auth.configured",{workspaceId:safeCreds.workspaceId,organizationId:safeCreds.organizationId,agentRecordId:opts?.agentRecordId});return(async()=>{try{if(await pluginManager.provisionDefaultPrereqs(app,hostServices),getDaemonProfile().logger.info("app","Default provider prerequisites provisioned"),(serviceRegistry.listProvidersForType("tunnel")??[]).some((p)=>p.pluginName!=="bootstrap")){let{tryHandoverBootstrapTunnel}=await import("./tunnel-bootstrap-x82wcnt8.js");if(!tryHandoverBootstrapTunnel())stopBootstrapTunnel();serviceRegistry.unregisterProvider("tunnel","bootstrap"),getDaemonProfile().logger.info("app","Bootstrap tunnel handed over to the real tunnel provider")}}catch(err){getDaemonProfile().logger.warn("app","Plugin prerequisite provisioning failed",{error:String(err)})}})(),{ok:!0}}catch(err){getDaemonProfile().setBootState("awaiting-config");let msg=err instanceof Error?err.message:String(err);if(getDaemonProfile().recordFinalizeError(msg),getDaemonProfile().logger.error("app","Finalize failed",{error:msg}),process.env.VIBE_STRICT_KEY_FETCH==="1")getDaemonProfile().logger.error("app","VIBE_STRICT_KEY_FETCH=1 \u2192 aborting daemon (strict mode)",{error:msg}),setTimeout(()=>process.exit(1),50);return{ok:!1,error:msg}}finally{finalizeInFlight=null}})(),finalizeInFlight},corsPlugin=cors({origin:(request)=>{let origin=request.headers.get("origin");return!!origin&&isAllowedOrigin(origin)},credentials:!0,allowedHeaders:CORS_HEADERS.split(",").map((s)=>s.trim()),methods:CORS_METHODS.split(",").map((s)=>s.trim()),exposeHeaders:["content-type","content-length"],maxAge:86400,preflight:!0}),DEFAULT_MAX_BODY_BYTES=10485760,maxBodyEnv=process.env.VIBECONTROLS_MAX_BODY_BYTES,parsedMaxBody=maxBodyEnv?Number.parseInt(maxBodyEnv,10):NaN,maxBodySize=Number.isFinite(parsedMaxBody)&&parsedMaxBody>0?parsedMaxBody:DEFAULT_MAX_BODY_BYTES,appRef={current:null},app=new Elysia({serve:{maxRequestBodySize:maxBodySize}}).onRequest(({request,set})=>{let requestId=extractRequestId(request.headers)??generateRequestId();enterRequestContext({requestId,startedAt:new Date().toISOString()});let headers=set.headers??={};headers["X-Request-Id"]=requestId}).onRequest(({request})=>{try{let url=new URL(request.url).pathname,name=/^\/api\/profiles\/([^/]+)(?:\/|$)/.exec(url)?.[1],ctx=name?profileRegistry.get(name):null;enterProfileContext(ctx??getDaemonProfile())}catch{enterProfileContext(getDaemonProfile())}}).onRequest(({request,set})=>{let url=new URL(request.url).pathname;if(!url.startsWith("/api/"))return;if(url==="/api/profiles"||url.startsWith("/api/profiles/"))return;if(url==="/api/profile-stats"||url.startsWith("/api/profile-stats/"))return;let defaultProfileUrl=new URL(request.url);defaultProfileUrl.pathname=`/api/profiles/default${url.slice(4)}`;try{request.headers.set("x-vc-profile-rewrite","1"),request.headers.set("x-vc-profile-rewrite-target",defaultProfileUrl.pathname)}catch{}return}).derive(()=>({requestId:getRequestId()??generateRequestId()})).use(corsPlugin).use(rateLimitPlugin()).use(createAuthPlugin(()=>pluginPublicPaths())).use(idempotencyPlugin({getDb:()=>currentProfileOrNull()?.db??db})).onBeforeHandle(({request,set})=>{let state=getDaemonProfile().getBootState();if(state==="ready")return;let url=new URL(request.url).pathname;if(isFinalizeExempt(url))return;return set.status=503,{error:"Agent not yet configured",message:"Agent is in '"+state+"' state. POST credentials to /api/agent/gateway-auth to finalize.",state}}).onAfterHandle(({request,set})=>{let url=new URL(request.url).pathname,isNoisy=url==="/health"||url.startsWith("/api/logs/stream")||url.startsWith("/ui/")||url==="/ws/events"||url==="/api/sessions/health-check"||url.startsWith("/terminal/"),status=typeof set.status==="number"?set.status:200,level=isNoisy?"debug":status>=400?"warn":"info";getDaemonProfile().logger[level]("http",`${request.method} ${url} \u2192 ${status}`,{method:request.method,path:url,statusCode:status});try{recordHttpRequest(request.method,normalizeRouteLabel(url),status)}catch{}}).use(createHealthRoutes({serviceRegistry,getDb:()=>currentProfileOrNull()?.db??db,getDbPath:()=>effectiveDbPath,getPluginManager:()=>currentProfileOrNull()?.pluginManager??pluginManager})).use(createMetricsRoutes()).use(createPreConfigRoutes({serviceRegistry,runFinalize:(creds,opts)=>finalize(creds,opts),getDb:()=>currentProfileOrNull()?.db??db})).use(createInflightTracker()).use(createStatsRoutes()).use(createEditorRoutes()).use(createContextRoutes({getPluginManager:()=>currentProfileOrNull()?.pluginManager??pluginManager,getTunnelHost:()=>readTunnelState()?.url??null})).use(createProfileManagementRoutes()).use(createProfileStatsRoutes()).use(createEventsWs()).use(createLogWs()).use(createTerminalProxy(serviceRegistry)).ws("/code-server/*",buildCodeServerBridge(port)).all("/api/profiles/:name/*",async({params,request,set})=>{let hasReqBody=request.method!=="GET"&&request.method!=="HEAD",bodyBytes=hasReqBody?await request.arrayBuffer():void 0,dispatched=await dispatchProfileRequest(params.name,request,bodyBytes);if(dispatched&&dispatched.status!==404)return dispatched;let running=getVibecontrolsProfile(),requested=params.name;if(requested!==running)return set.status=404,{error:"profile-mismatch",running,requested,hint:"This daemon serves a different profile. Call GET /api/profiles to discover the right tunnel."};let u=new URL(request.url),stripped=u.pathname.replace(/^\/api\/profiles\/[^/]+/,"/api"),headers=new Headers(request.headers);headers.set("x-vc-profile-rewrite","1");let init={method:request.method,headers};if(hasReqBody&&bodyBytes!==void 0)init.body=bodyBytes;let rewritten=new Request(`${u.origin}${stripped}${u.search}`,init),target=appRef.current;if(!target)return new Response(JSON.stringify({error:"agent-not-ready"}),{status:503,headers:{"content-type":"application/json"}});return target.handle(rewritten)}).all("/api/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!pluginRoutesApp)return new Response(JSON.stringify({error:"Agent not yet configured",state:getDaemonProfile().getBootState(),message:"Plugin routes are unavailable until the agent reaches state=ready. POST credentials to /api/agent/gateway-auth to finalize."}),{status:503,headers:{"content-type":"application/json"}});try{let apiUiUrl=new URL(request.url),apiUiMatch=/^\/api\/plugins\/([a-z0-9-]{1,64})\/ui(?:\/.*)?$/.exec(apiUiUrl.pathname);if(apiUiMatch&&request.method==="GET"){let plugin=apiUiMatch[1];if(isValidUiPluginName(plugin)){let restAfterUi=apiUiUrl.pathname.slice(`/api/plugins/${plugin}/ui`.length),isBareDoc=restAfterUi===""||restAfterUi==="/",looksLikeNavigation=request.headers.get("sec-fetch-mode")==="navigate";if(isBareDoc||looksLikeNavigation){let cookie=getUiCookieFromRequest(request,plugin);if(!cookie||!verifyUiCookieToken(cookie,plugin))return new Response(renderUiBootstrapHtml(plugin,"/api/profiles/default/agent/ui-exchange-plugin"),{status:200,headers:{"content-type":"text/html; charset=utf-8","Content-Security-Policy":"frame-ancestors 'self' http://localhost:* http://127.0.0.1:* https://localhost:* https://127.0.0.1:* https://*.local.burdenoff.com https://vibecontrols.com https://*.vibecontrols.com https://burdenoff.com https://app.burdenoff.com https://alphaapp.burdenoff.com"}})}}}}catch{}return pluginRoutesApp.handle(request)}).all("/ui/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!uiServerApp)return new Response("Plugin UI not yet available",{status:503});return uiServerApp.handle(request)}).all("/code-server/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!pluginRoutesApp)return new Response("code-server not yet available",{status:503});return pluginRoutesApp.handle(request)}).all("/plan/*",async({request})=>{if(getDaemonProfile().getBootState()!=="ready"||!pluginRoutesApp)return new Response("plan provider not yet available",{status:503});return pluginRoutesApp.handle(request)});return appRef.current=app,{app,async start(){app.listen({port,hostname:host}),getDaemonProfile().logger.info("app",`Agent server listening on ${host}:${port}`);try{let list=(process.env.VIBECONTROLS_PROFILES??"").split(",").map((s)=>s.trim()).filter(Boolean),bootName=getDaemonProfile().name,{getOrCreateProfile:getOrCreateProfile2}=await import("./daemon-profile-c7zt62gk.js"),{attachSecondaryProfile}=await import("./secondary-profile-attach-k49k65x7.js"),strict=process.env.VIBE_STRICT_KEY_FETCH==="1";for(let name of list){if(name===bootName)continue;try{let ctx=getOrCreateProfile2(name),result=await attachSecondaryProfile(ctx);if(result.ok)getDaemonProfile().logger.info("app",`Secondary profile '${name}' attached (db+services online)`);else if(strict)throw Error(`attachServices for secondary profile '${name}' failed: ${result.error}`);else ctx.recordDegradedReason("attach",result.error??"unknown"),getDaemonProfile().logger.warn("app",`Secondary profile '${name}' is degraded (no creds / attach failed)`,{error:result.error})}catch(err){if(strict)throw getDaemonProfile().logger.error("app",`--strict-key-fetch: secondary profile '${name}' attach failed; aborting`,{error:String(err)}),setTimeout(()=>process.exit(1),50),err;getDaemonProfile().logger.warn("app",`Failed to attach secondary profile '${name}'`,{error:String(err)})}}}catch{}return app},stop:stopFn,finalize,getState:()=>getDaemonProfile().getBootState(),getLifecycle:()=>currentProfileOrNull()?.lifecycle??lifecycle,getDb:()=>currentProfileOrNull()?.db??db,getServiceRegistry:()=>currentProfileOrNull()?.serviceRegistry??serviceRegistry,getPluginManager:()=>currentProfileOrNull()?.pluginManager??pluginManager,getHostServices:()=>currentProfileOrNull()?.hostServices??hostServices}}var codeServerSessionValidator=null;function getCookieValue(cookieHeader,name){if(!cookieHeader)return null;let m=cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));return m?m[1]:null}function buildCodeServerBridge(port){let payloadBytes=(payload)=>typeof payload==="string"?Buffer.byteLength(payload):payload.byteLength;return{open(ws){let wsData=ws.data,apiKey=wsData.query?.apiKey??getApiKeyFromHeaders(wsData.headers??{})??getApiKeyFromHeaders(wsData.request?Object.fromEntries(wsData.request.headers.entries()):{});if(!isValidAgentApiKey(apiKey)){let cookieHeader=wsData.headers?.cookie??wsData.request?.headers.get("cookie")??null,sessionToken=getCookieValue(cookieHeader,"__vibe_cs_session");if(!sessionToken||!codeServerSessionValidator?.(sessionToken)){ws.close(1008,"Unauthorized");return}}let bridgeProfile=getDaemonProfile().name;fetch(`http://127.0.0.1:${port}/api/profiles/${encodeURIComponent(bridgeProfile)}/code-server/status`,{headers:{"x-agent-api-key":getAgentApiKey()}}).then((r)=>r.json()).then((raw)=>{if(wsData._csClosed)return;let status=raw;if(!status.running||!status.port){ws.close(1011,"code-server not running");return}let requestUrl=wsData.request?.url??"/code-server/",upstreamPath;try{let url=new URL(requestUrl,`http://127.0.0.1:${status.port}`);upstreamPath=(url.pathname.replace(/^\/code-server\/?/,"/")||"/")+url.search}catch{upstreamPath="/"}let upstreamWs=new WebSocket(`ws://127.0.0.1:${status.port}${upstreamPath}`),state={upstream:upstreamWs,ready:!1,buffer:[],bufferBytes:0,openTimer:setTimeout(()=>{try{upstreamWs.close(1011,"upstream open timeout")}catch{}try{ws.close(1011,"upstream open timeout")}catch{}},1e4)};wsData._csBridge=state,upstreamWs.addEventListener("open",()=>{clearTimeout(state.openTimer),state.ready=!0;for(let msg of state.buffer)upstreamWs.send(msg);state.buffer.length=0,state.bufferBytes=0}),upstreamWs.addEventListener("message",(event)=>{try{let d=event.data;if(d instanceof ArrayBuffer)ws.send(new Uint8Array(d));else if(d instanceof Blob)d.arrayBuffer().then((ab)=>{try{ws.send(new Uint8Array(ab))}catch{}});else ws.send(d)}catch{}}),upstreamWs.addEventListener("close",(event)=>{clearTimeout(state.openTimer);try{ws.close(event.code||1000,event.reason||"upstream closed")}catch{}}),upstreamWs.addEventListener("error",()=>{clearTimeout(state.openTimer);try{ws.close(1011,"upstream error")}catch{}})}).catch(()=>{ws.close(1011,"Failed to query code-server status")})},message(ws,message){let state=ws.data?._csBridge;if(!state)return;let payload;if(typeof message==="string")payload=message;else if(message instanceof ArrayBuffer)payload=message;else if(message instanceof Uint8Array||Buffer.isBuffer(message)){let copy=new ArrayBuffer(message.byteLength);new Uint8Array(copy).set(new Uint8Array(message.buffer,message.byteOffset,message.byteLength)),payload=copy}else if(typeof message==="object"&&message!==null)payload=JSON.stringify(message);else payload=String(message);if(state.upstream&&state.ready&&state.upstream.readyState===WebSocket.OPEN)try{state.upstream.send(payload)}catch{}else{let nextBytes=payloadBytes(payload);if(state.buffer.length>=256||state.bufferBytes+nextBytes>1048576){try{state.upstream.close(1013,"buffer limit exceeded")}catch{}try{ws.close(1013,"buffer limit exceeded")}catch{}return}state.buffer.push(payload),state.bufferBytes+=nextBytes}},close(ws){ws.data._csClosed=!0;let state=ws.data?._csBridge;if(state?.openTimer)clearTimeout(state.openTimer);if(state?.upstream&&state.upstream.readyState===WebSocket.OPEN)state.upstream.close(1000,"client disconnected")}}}async function getPackageVersion(){try{let{readFileSync:readFileSync3}=await import("fs"),{join:join4,dirname:dirname2}=await import("path"),{fileURLToPath}=await import("url"),__dirname2=dirname2(fileURLToPath(import.meta.url));return JSON.parse(readFileSync3(join4(__dirname2,"..","package.json"),"utf8")).version||"1.0.0"}catch{return"1.0.0"}}
|
|
5
5
|
export{mintPairingToken,invalidatePairingToken,createApp};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{getPluginPackageRoot}from"./index-
|
|
2
|
+
import{getPluginPackageRoot}from"./index-h6a1kkfk.js";import{getAgentApiKey,getIframeTokenFromRequest,getUiCookieFromRequest,isValidUiPluginName,issueUiCookieToken,verifyIframeToken,verifyUiCookieToken}from"./index-b4wy3jrt.js";import{Elysia}from"./index-rnk0kny8.js";class CliContributorRegistry{statusSections=[];doctorChecks=[];addStatusSection(section){this.statusSections.push(section)}addDoctorCheck(check){this.doctorChecks.push(check)}getStatusSections(){return this.statusSections}getDoctorChecks(){return this.doctorChecks}}var activeRegistry=null;function setActiveContributorRegistry(registry){activeRegistry=registry}function getActiveContributorRegistry(){if(!activeRegistry)activeRegistry=new CliContributorRegistry;return activeRegistry}import{extname,relative,resolve}from"path";import{realpath}from"fs/promises";var DEFAULT_FRAME_ANCESTORS=["'self'","http://localhost:*","http://127.0.0.1:*","https://localhost:*","https://127.0.0.1:*","https://*.local.burdenoff.com","https://vibecontrols.com","https://*.vibecontrols.com","https://burdenoff.com","https://app.burdenoff.com","https://alphaapp.burdenoff.com"],warnedAboutUnsafeOverride=!1;function frameAncestorsCsp(){let override=process.env.VIBE_UI_FRAME_ANCESTORS?.trim(),sources=override?override.split(/[\s,]+/).filter(Boolean):DEFAULT_FRAME_ANCESTORS;if(override&&!warnedAboutUnsafeOverride&&(sources.includes("*")||!sources.includes("'self'")))warnedAboutUnsafeOverride=!0,console.warn(`[ui-server] VIBE_UI_FRAME_ANCESTORS override does not include "'self'" or contains "*". This significantly weakens CSP frame-ancestors and is almost never intended. Override: ${override}`);return`frame-ancestors ${sources.join(" ")}`}var MIME_TYPES={".html":"text/html",".js":"application/javascript",".mjs":"application/javascript",".css":"text/css",".json":"application/json",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".svg":"image/svg+xml",".ico":"image/x-icon",".woff":"font/woff",".woff2":"font/woff2",".ttf":"font/ttf",".map":"application/json",".wasm":"application/wasm"};function getMimeType(filePath){return MIME_TYPES[extname(filePath).toLowerCase()]||"application/octet-stream"}var AUTH_BRIDGE_SCRIPT=`<script>
|
|
3
3
|
window.__VIBE_AUTH__={apiKey:null};
|
|
4
4
|
(function(){
|
|
5
5
|
var p=new URLSearchParams(window.location.search);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{readPrereqState,runPluginPrereqs}from"./index-9nn2kpyg.js";import{notifyBackendPluginChange}from"./index-zww580wx.js";import"./index-23rdsqea.js";import"./index-a5ta2sm5.js";import{AVAILABLE_PLUGINS,getCatalogEntry,isCriticalPlugin,isTrustedPackage,validatePluginPackageName}from"./index-vvp9h4hs.js";import"./index-srbb2214.js";import"./index-brtw3j8x.js";import"./index-b4wy3jrt.js";import{INTERNAL_DISPATCH_HEADER,getInternalDispatchToken}from"./index-d3mz9vws.js";import{Elysia,t}from"./index-rnk0kny8.js";import{apiGet,apiPost,getAgentUrl,maybePrintJson,pickOutputMode,resolveProfileName,runMultimode}from"./index-wvabz3xh.js";import{colors,errMsg,fail,formatTable,header,info,kv,success,warn}from"./index-r0qezdp7.js";import{interactiveDetail,interactiveTable}from"./index-campp0wv.js";import"./index-2pqv0bya.js";import{getDaemonProfile}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import{__require}from"./index-e9rt4m94.js";import{timingSafeEqual}from"crypto";async function getRegistryIntegrity(pkg,version){try{let proc=Bun.spawn({cmd:["npm","view",`${pkg}@${version}`,"dist.integrity","--json"],stdout:"pipe",stderr:"pipe"});if(await proc.exited!==0)return null;let trimmed=(await new Response(proc.stdout).text()).trim();if(!trimmed)return null;if(trimmed.startsWith('"'))try{let parsed=JSON.parse(trimmed);return typeof parsed==="string"?parsed:null}catch{return null}return trimmed}catch{return null}}function compareIntegrity(expected,actual){let a=Buffer.from(expected),b=Buffer.from(actual);if(a.length!==b.length)return!1;try{return timingSafeEqual(a,b)}catch{return!1}}var AVAILABLE_PLUGINS2=AVAILABLE_PLUGINS.map((p)=>({packageName:p.packageName,name:p.pluginName,description:p.description,cliCommand:p.cliCommand,apiPrefix:p.apiPrefix,installed:!1,category:p.category})),lifecycleLocks=new Map;function withPluginLifecycleLock(packageName,fn){let run=(lifecycleLocks.get(packageName)??Promise.resolve()).then(fn,fn),stored=run.then(()=>{return},()=>{return});return lifecycleLocks.set(packageName,stored),stored.finally(()=>{if(lifecycleLocks.get(packageName)===stored)lifecycleLocks.delete(packageName)}),run}function isPackageInstallInFlight(packageName){return lifecycleLocks.has(packageName)}function availablePluginsPayload(pluginManager){let installed=pluginManager.getPluginDetails(),installedNames=new Set(installed.map((p)=>p.packageName));return{plugins:AVAILABLE_PLUGINS2.map((p)=>({...p,installed:installedNames.has(p.packageName)}))}}function createRoutes(deps){let{pluginManager}=deps;return new Elysia().get("/",()=>{let plugins=pluginManager.getPluginDetails();return{plugins,count:plugins.length}}).get("/available",()=>availablePluginsPayload(pluginManager)).get("/catalog",()=>availablePluginsPayload(pluginManager)).get("/contributions",({query})=>{let wantedMount=typeof query?.mountPoint==="string"&&query.mountPoint?query.mountPoint:null,items=[],packageNameByPluginName=new Map;for(let detail of pluginManager.getPluginDetails())packageNameByPluginName.set(detail.pluginName,detail.packageName);for(let plugin of pluginManager.getAllPlugins()){let ui=plugin.ui;if(!ui||!ui.contributions||ui.contributions.length===0)continue;let fallbackCaps=ui.capabilities??{restPaths:[],wsTopics:[],rpcMethods:[]};for(let contribution of ui.contributions){if(wantedMount&&contribution.mountPoint!==wantedMount)continue;let meta=contribution.meta,rawOverride=meta&&typeof meta.url==="string"?meta.url:null,overrideUrl=rawOverride&&rawOverride.startsWith("/")&&!rawOverride.startsWith("//")&&!rawOverride.includes("\x00")&&!rawOverride.includes("..")?rawOverride:null;items.push({pluginKey:plugin.name,pluginPackageName:packageNameByPluginName.get(plugin.name),contribution,capabilities:contribution.capabilities??fallbackCaps,url:overrideUrl??`/ui/${encodeURIComponent(plugin.name)}`})}}return{contributions:items,count:items.length}}).post("/install",async({body,set})=>{if(!body.packageName||typeof body.packageName!=="string")return set.status=400,{error:"Missing required field: packageName"};if(body.packageName.startsWith("@burdenoff/"))body.packageName=body.packageName.replace(/^@burdenoff\//,"@vibecontrols/");if(!isTrustedPackage(body.packageName))return set.status=403,{error:"Plugin package is not in the trusted VibeControls catalog"};try{let packageName=validatePluginPackageName(body.packageName);if(isPackageInstallInFlight(packageName)){let slug=packageName.split("/").pop()?.replace("vibe-plugin-","");if(pluginManager.getAllPlugins().some((p)=>p.name===slug))return{success:!0,message:`Plugin ${packageName} is already installed`,packageName,alreadyInstalled:!0};return set.status=409,{error:"Install for this package is already in progress. Wait for it to finish.",packageName}}return await withPluginLifecycleLock(packageName,async()=>{let pluginName=packageName.split("/").pop()?.replace("vibe-plugin-",""),legacyAiProviderName=pluginName?.startsWith("ai-")?pluginName.slice(3):void 0,existingPlugin=pluginManager.getAllPlugins().find((p)=>p.name===pluginName||p.name===legacyAiProviderName),alreadyRunning=!!existingPlugin,installedEntry=await pluginManager.install(packageName),catalogEntry=getCatalogEntry(packageName);if(catalogEntry?.integrity){let actualIntegrity=await getRegistryIntegrity(packageName,installedEntry.version);if(!actualIntegrity||!compareIntegrity(catalogEntry.integrity,actualIntegrity)){try{await pluginManager.remove(packageName,deps.hostServices)}catch(rollbackErr){console.warn(`[plugin-mgr] integrity rollback uninstall failed for ${packageName}:`,rollbackErr)}return getDaemonProfile().audit.emit("agent","plugin.install.rejected",{packageName,version:installedEntry.version,reason:"integrity_mismatch",expected:catalogEntry.integrity,actual:actualIntegrity}),set.status=400,{error:"integrity mismatch",expected:catalogEntry.integrity,actual:actualIntegrity}}}else console.warn(`[plugin-mgr] plugin ${packageName} has no pinned integrity \u2014 accepting npm registry's hash`);let newPlugin=pluginManager.getAllPlugins().find((p)=>p.name===installedEntry.pluginName);if(newPlugin){let lifecycleErrors=[];if(deps.mountPlugin)deps.mountPlugin(newPlugin);if(deps.hostServices){if(alreadyRunning&&existingPlugin?.onServerStop)try{await existingPlugin.onServerStop()}catch(err){lifecycleErrors.push(`onServerStop failed: ${err instanceof Error?err.message:String(err)}`)}if(pluginManager.registerProviders(newPlugin,deps.hostServices),newPlugin.onServerStart&&deps.app)try{await newPlugin.onServerStart(deps.app,deps.hostServices),pluginManager.registerProviders(newPlugin,deps.hostServices)}catch(err){lifecycleErrors.push(`onServerStart failed: ${err instanceof Error?err.message:String(err)}`)}if(newPlugin.onServerReady&&deps.app)try{await newPlugin.onServerReady(deps.app,deps.hostServices)}catch(err){lifecycleErrors.push(`onServerReady failed: ${err instanceof Error?err.message:String(err)}`)}}if(deps.rebuildPluginSurfaces?.(),lifecycleErrors.length>0)return set.status=500,{success:!1,message:`Plugin ${packageName} installed but failed to start`,lifecycleErrors,restartRequired:!0}}let prereqs;if(newPlugin&&deps.app)try{prereqs=await runPluginPrereqs(newPlugin,packageName,deps.app,deps.db,{skipInstall:!!body.skipPrereqs})}catch(err){prereqs=void 0,console.warn(`[plugin-mgr] prereq run failed for ${packageName}:`,err)}return notifyBackendPluginChange(deps.db),getDaemonProfile().audit.emit("agent","plugin.installed",{packageName,version:installedEntry.version,pluginName:installedEntry.pluginName,wasRunning:alreadyRunning}),{success:!0,message:`Plugin ${packageName} installed and loaded successfully`,prereqs:prereqs?{satisfied:prereqs.satisfied,missing:prereqs.status?.missing??[],pendingSudo:prereqs.pendingSudo,skipped:!!body.skipPrereqs}:void 0}})}catch(err){return set.status=500,{error:"Failed to install plugin",details:String(err)}}},{body:t.Object({packageName:t.String(),skipPrereqs:t.Optional(t.Boolean())})}).post("/prereqs/status",async({body,set})=>{if(!body.packageName)return set.status=400,{error:"Missing packageName"};let pkg=body.packageName,plugin=pluginManager.getAllPlugins().find((p)=>p.name===pkg||pluginManager.getLoaded(pkg)?.name===p.name);if(!plugin||!deps.app)return await readPrereqState(deps.db,pkg)??{satisfied:!0,missing:[]};try{let r=await runPluginPrereqs(plugin,pkg,deps.app,deps.db,{skipInstall:!0});return{satisfied:r.satisfied,missing:r.status?.missing??[],noProtocol:r.noProtocol}}catch(err){return set.status=500,{error:String(err)}}},{body:t.Object({packageName:t.String()})}).post("/prereqs/install",async({body,set})=>{if(!body.packageName)return set.status=400,{error:"Missing packageName"};let pkg=body.packageName,plugin=pluginManager.getAllPlugins().find((p)=>p.name===pkg||pluginManager.getLoaded(pkg)?.name===p.name);if(!plugin||!deps.app)return set.status=404,{error:"Plugin not loaded"};try{let r=await runPluginPrereqs(plugin,pkg,deps.app,deps.db,{skipInstall:!1});return{satisfied:r.satisfied,installed:r.install?.installed??[],pendingSudo:r.pendingSudo,errors:r.install?.errors??[],noProtocol:r.noProtocol}}catch(err){return set.status=500,{error:String(err)}}},{body:t.Object({packageName:t.String(),approveSudo:t.Optional(t.Boolean())})}).post("/prereqs/uninstall",async({body,set})=>{if(!body.packageName)return set.status=400,{error:"Missing packageName"};let pkg=body.packageName,plugin=pluginManager.getAllPlugins().find((p)=>p.name===pkg);if(!plugin||!plugin.apiPrefix||!deps.app)return set.status=404,{error:"Plugin not loaded"};let handle=deps.app.handle;if(typeof handle!=="function")return set.status=500,{error:"App handle unavailable"};let path=`${plugin.apiPrefix.replace(/\/+$/,"")}/prereqs/uninstall`,res=await handle.call(deps.app,new Request(`http://agent.local${path}`,{method:"POST",headers:{"x-vc-profile-rewrite":"1",[INTERNAL_DISPATCH_HEADER]:getInternalDispatchToken()}}));if(res.status===404)return set.status=404,{error:"Plugin has no prereqs/uninstall endpoint"};return res.json()},{body:t.Object({packageName:t.String()})}).post("/nuke",async({body,set})=>{if(!deps.hostServices)return set.status=503,{error:"hostServices unavailable"};let agentDir=deps.hostServices.getDataDir?.();return{ok:!0,...await pluginManager.runPluginNuke(body.packageName,deps.hostServices,{agentDir,dryRun:!!body.dryRun})}},{body:t.Object({packageName:t.Optional(t.String()),dryRun:t.Optional(t.Boolean())})}).post("/remove",async({body,set})=>{if(!body.packageName||typeof body.packageName!=="string")return set.status=400,{error:"Missing required field: packageName"};if(body.packageName.startsWith("@burdenoff/"))body.packageName=body.packageName.replace(/^@burdenoff\//,"@vibecontrols/");if(isCriticalPlugin(body.packageName))return{success:!0,message:`Plugin ${body.packageName} is critical and cannot be removed; treating remove as a no-op.`,skipped:!0};try{let packageName=validatePluginPackageName(body.packageName);return await withPluginLifecycleLock(packageName,async()=>{return await pluginManager.remove(packageName,deps.hostServices),deps.rebuildPluginSurfaces?.(),notifyBackendPluginChange(deps.db),getDaemonProfile().audit.emit("agent","plugin.removed",{packageName}),{success:!0,message:`Plugin ${packageName} removed successfully`}})}catch(err){return set.status=500,{error:"Failed to remove plugin",details:String(err)}}},{body:t.Object({packageName:t.String()})}).post("/update",async({body,set})=>{if(!body.packageName||typeof body.packageName!=="string")return set.status=400,{error:"Missing required field: packageName"};try{let packageName=validatePluginPackageName(body.packageName);return await withPluginLifecycleLock(packageName,async()=>{let existingPlugin=pluginManager.getLoaded(packageName);if(existingPlugin?.onServerStop)await existingPlugin.onServerStop({reason:"reload"});if(existingPlugin&&deps.hostServices)deps.hostServices.serviceRegistry.unregisterPlugin(existingPlugin.name);let entry=await pluginManager.update(packageName),updatedPlugin=pluginManager.getLoaded(packageName);if(updatedPlugin&&deps.hostServices){if(pluginManager.registerProviders(updatedPlugin,deps.hostServices),updatedPlugin.onServerStart&&deps.app)await updatedPlugin.onServerStart(deps.app,deps.hostServices),pluginManager.registerProviders(updatedPlugin,deps.hostServices);if(updatedPlugin.onServerReady&&deps.app)await updatedPlugin.onServerReady(deps.app,deps.hostServices)}return deps.rebuildPluginSurfaces?.(),notifyBackendPluginChange(deps.db),{success:!0,message:`Plugin ${packageName} updated to v${entry.version}`,version:entry.version}})}catch(err){return set.status=500,{error:"Failed to update plugin",details:String(err)}}},{body:t.Object({packageName:t.String()})}).post("/update-all",async({set})=>{try{let results=await withPluginLifecycleLock("*",()=>pluginManager.updateAll());return{success:!0,results,message:`Updated ${results.filter((r)=>r.success).length}/${results.length} plugin(s)`}}catch(err){return set.status=500,{error:"Failed to update all plugins",details:String(err)}}}).post("/remove-all",async({set})=>{try{let results=await withPluginLifecycleLock("*",async()=>{let removed=await pluginManager.removeAll(deps.hostServices);return deps.rebuildPluginSurfaces?.(),removed});return{success:!0,results,message:`Removed ${results.filter((r)=>r.success).length}/${results.length} plugin(s)`}}catch(err){return set.status=500,{error:"Failed to remove all plugins",details:String(err)}}}).post("/reload",async({set})=>{try{if(!deps.app||!deps.hostServices)return set.status=503,{error:"Plugin lifecycle services are not ready"};let plugins=await withPluginLifecycleLock("*",async()=>{return await pluginManager.reloadAll(deps.app,deps.hostServices),deps.rebuildPluginSurfaces?.(),pluginManager.getPluginDetails()});return{success:!0,plugins,message:`Reloaded ${plugins.length} plugin(s)`}}catch(err){return set.status=500,{error:"Failed to reload plugins",details:String(err)}}}).post("/:name/reload",async({params,set})=>{if(!deps.app||!deps.hostServices)return set.status=503,{error:"Plugin lifecycle services are not ready"};let target=params.name,plugin=pluginManager.getAllPlugins().find((p)=>p.name===target);if(!plugin)return set.status=404,{error:`Plugin "${target}" not found`};let packageName=pluginManager.getPluginDetails().find((d)=>d.pluginName===target)?.packageName??`core:${target}`;if(isPackageInstallInFlight(packageName))return set.status=409,{error:"Plugin lifecycle in progress; try again shortly",packageName};try{return await withPluginLifecycleLock(packageName,async()=>{if(plugin.onServerStop)try{await plugin.onServerStop({reason:"reload"})}catch(err){console.warn(`[plugin-mgr] onServerStop during reload of ${target}:`,err)}if(deps.hostServices)deps.hostServices.serviceRegistry.unregisterPlugin(plugin.name);if(pluginManager.registerProviders(plugin,deps.hostServices),plugin.onServerStart)await plugin.onServerStart(deps.app,deps.hostServices),pluginManager.registerProviders(plugin,deps.hostServices);if(plugin.onServerReady)await plugin.onServerReady(deps.app,deps.hostServices);return deps.rebuildPluginSurfaces?.(),{success:!0,message:`Plugin ${target} reloaded`,version:plugin.version}})}catch(err){return set.status=500,{error:"Failed to reload plugin",details:String(err)}}},{params:t.Object({name:t.String()})})}import{mkdirSync,writeFileSync,existsSync}from"fs";import{isAbsolute,join,relative,resolve}from"path";var DEFAULT_AGENT_URL="http://localhost:3005",PLUGIN_BASENAME_RE=/^[a-z0-9][a-z0-9-]{0,63}$/,ALLOWED_TAGS=new Set(["backend","frontend","cli","provider","adapter","integration"]);function assertInside(parent,child){let rel=relative(parent,child);if(rel.startsWith("..")||isAbsolute(rel))throw Error("Plugin scaffold path escapes the selected directory.")}function toLiteral(value){return JSON.stringify(value)}function register(program){let cmd=program.command("plugin").description("Manage plugins");cmd.command("list").description("List installed plugins").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON").option("--plain","Force plain text output").action(async(options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged)),fetchData=async()=>{try{return(await apiGet(url,`/api/profiles/${profile}/plugins`)).plugins||[]}catch{warn("Agent not reachable. Attempting local plugin registry...");let{PluginManager}=await import("./plugin-system-vv1y4xm1.js");return new PluginManager().getPluginDetails()||[]}},statusOf=(p)=>p.status??(p.loaded===!0||p.enabled===!0?"enabled":"disabled"),nameOf=(p)=>p.pluginName||p.name||p.packageName||p.package||"-";await runMultimode({mode:pickOutputMode(merged),fetchData,plain:(list)=>{if(!list.length){info("No plugins installed.");return}header("Installed Plugins"),formatTable(list.map((p)=>({Name:nameOf(p),Version:p.version||"-",Status:statusOf(p),Description:p.description||"-"})))},interactive:async(list)=>{if(!list.length){header("Installed Plugins"),info("No plugins installed.");return}let rows=list.map((p)=>{let name=nameOf(p),status=statusOf(p),detailLines=[`${colors.bold(name)} ${status==="enabled"?colors.green(status):colors.dim(status)}`,"",` Version: ${p.version||"-"}`,` Description: ${p.description||"-"}`];if(p.tags&&Array.isArray(p.tags))detailLines.push(` Tags: ${p.tags.join(", ")}`);if(p.cliCommand)detailLines.push(` CLI command: vibe ${p.cliCommand}`);return{id:name,label:name,hint:status,detail:detailLines.join(`
|
|
3
|
-
`)}});await interactiveTable({title:`vibe plugin \u2014 ${list.length} installed`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"})},json:(list)=>list.map((p)=>({name:nameOf(p),version:p.version??null,status:statusOf(p),description:p.description??null,tags:p.tags??null,cliCommand:p.cliCommand??null}))})}catch(err){fail(errMsg(err))}}),cmd.command("install").description("Install a plugin").argument("<package>","NPM package name to install").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--skip-prereqs","Skip running the plugin's prereqs/install",!1).option("--json","Emit JSON result").action(async(packageName,options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged));try{let result=await apiPost(url,`/api/profiles/${profile}/plugins/install`,{packageName,skipPrereqs:!!options.skipPrereqs});if(maybePrintJson(merged,{ok:!0,action:"install",package:packageName,version:result?.version??null,prereqs:result?.prereqs??null}))return;if(success(`Plugin "${packageName}" installed.`),result?.version)kv("Version",result.version);if(result?.prereqs){let p=result.prereqs;if(p.skipped)warn(`Prerequisite install skipped. Run \`vibe plugin prereqs ${packageName} install\` later.`);else if(!p.satisfied)warn(`Prerequisites not yet satisfied: ${(p.missing||[]).map((m)=>m.name).join(", ")||"(unknown)"}`);if((p.pendingSudo||[]).length>0){warn(`${p.pendingSudo.length} command(s) need sudo. Run them and re-check with \`vibe plugin prereqs ${packageName} status\`:`);for(let ps of p.pendingSudo)kv(` ${ps.name}`,ps.command)}}}catch{warn("Agent not reachable. Attempting local install...");try{let{PluginManager}=await import("./plugin-system-
|
|
2
|
+
import{readPrereqState,runPluginPrereqs}from"./index-9nn2kpyg.js";import{notifyBackendPluginChange}from"./index-g9h64dn9.js";import"./index-23rdsqea.js";import"./index-n3y918j2.js";import{AVAILABLE_PLUGINS,getCatalogEntry,isCriticalPlugin,isTrustedPackage,validatePluginPackageName}from"./index-h6a1kkfk.js";import"./index-srbb2214.js";import"./index-brtw3j8x.js";import"./index-b4wy3jrt.js";import{INTERNAL_DISPATCH_HEADER,getInternalDispatchToken}from"./index-d3mz9vws.js";import{Elysia,t}from"./index-rnk0kny8.js";import{apiGet,apiPost,getAgentUrl,maybePrintJson,pickOutputMode,resolveProfileName,runMultimode}from"./index-wvabz3xh.js";import{colors,errMsg,fail,formatTable,header,info,kv,success,warn}from"./index-r0qezdp7.js";import{interactiveDetail,interactiveTable}from"./index-campp0wv.js";import"./index-2pqv0bya.js";import{getDaemonProfile}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import{__require}from"./index-e9rt4m94.js";import{timingSafeEqual}from"crypto";async function getRegistryIntegrity(pkg,version){try{let proc=Bun.spawn({cmd:["npm","view",`${pkg}@${version}`,"dist.integrity","--json"],stdout:"pipe",stderr:"pipe"});if(await proc.exited!==0)return null;let trimmed=(await new Response(proc.stdout).text()).trim();if(!trimmed)return null;if(trimmed.startsWith('"'))try{let parsed=JSON.parse(trimmed);return typeof parsed==="string"?parsed:null}catch{return null}return trimmed}catch{return null}}function compareIntegrity(expected,actual){let a=Buffer.from(expected),b=Buffer.from(actual);if(a.length!==b.length)return!1;try{return timingSafeEqual(a,b)}catch{return!1}}var AVAILABLE_PLUGINS2=AVAILABLE_PLUGINS.map((p)=>({packageName:p.packageName,name:p.pluginName,description:p.description,cliCommand:p.cliCommand,apiPrefix:p.apiPrefix,installed:!1,category:p.category})),lifecycleLocks=new Map;function withPluginLifecycleLock(packageName,fn){let run=(lifecycleLocks.get(packageName)??Promise.resolve()).then(fn,fn),stored=run.then(()=>{return},()=>{return});return lifecycleLocks.set(packageName,stored),stored.finally(()=>{if(lifecycleLocks.get(packageName)===stored)lifecycleLocks.delete(packageName)}),run}function isPackageInstallInFlight(packageName){return lifecycleLocks.has(packageName)}function availablePluginsPayload(pluginManager){let installed=pluginManager.getPluginDetails(),installedNames=new Set(installed.map((p)=>p.packageName));return{plugins:AVAILABLE_PLUGINS2.map((p)=>({...p,installed:installedNames.has(p.packageName)}))}}function createRoutes(deps){let{pluginManager}=deps;return new Elysia().get("/",()=>{let plugins=pluginManager.getPluginDetails();return{plugins,count:plugins.length}}).get("/available",()=>availablePluginsPayload(pluginManager)).get("/catalog",()=>availablePluginsPayload(pluginManager)).get("/contributions",({query})=>{let wantedMount=typeof query?.mountPoint==="string"&&query.mountPoint?query.mountPoint:null,items=[],packageNameByPluginName=new Map;for(let detail of pluginManager.getPluginDetails())packageNameByPluginName.set(detail.pluginName,detail.packageName);for(let plugin of pluginManager.getAllPlugins()){let ui=plugin.ui;if(!ui||!ui.contributions||ui.contributions.length===0)continue;let fallbackCaps=ui.capabilities??{restPaths:[],wsTopics:[],rpcMethods:[]};for(let contribution of ui.contributions){if(wantedMount&&contribution.mountPoint!==wantedMount)continue;let meta=contribution.meta,rawOverride=meta&&typeof meta.url==="string"?meta.url:null,overrideUrl=rawOverride&&rawOverride.startsWith("/")&&!rawOverride.startsWith("//")&&!rawOverride.includes("\x00")&&!rawOverride.includes("..")?rawOverride:null;items.push({pluginKey:plugin.name,pluginPackageName:packageNameByPluginName.get(plugin.name),contribution,capabilities:contribution.capabilities??fallbackCaps,url:overrideUrl??`/ui/${encodeURIComponent(plugin.name)}`})}}return{contributions:items,count:items.length}}).post("/install",async({body,set})=>{if(!body.packageName||typeof body.packageName!=="string")return set.status=400,{error:"Missing required field: packageName"};if(body.packageName.startsWith("@burdenoff/"))body.packageName=body.packageName.replace(/^@burdenoff\//,"@vibecontrols/");if(!isTrustedPackage(body.packageName))return set.status=403,{error:"Plugin package is not in the trusted VibeControls catalog"};try{let packageName=validatePluginPackageName(body.packageName);if(isPackageInstallInFlight(packageName)){let slug=packageName.split("/").pop()?.replace("vibe-plugin-","");if(pluginManager.getAllPlugins().some((p)=>p.name===slug))return{success:!0,message:`Plugin ${packageName} is already installed`,packageName,alreadyInstalled:!0};return set.status=409,{error:"Install for this package is already in progress. Wait for it to finish.",packageName}}return await withPluginLifecycleLock(packageName,async()=>{let pluginName=packageName.split("/").pop()?.replace("vibe-plugin-",""),legacyAiProviderName=pluginName?.startsWith("ai-")?pluginName.slice(3):void 0,existingPlugin=pluginManager.getAllPlugins().find((p)=>p.name===pluginName||p.name===legacyAiProviderName),alreadyRunning=!!existingPlugin,installedEntry=await pluginManager.install(packageName),catalogEntry=getCatalogEntry(packageName);if(catalogEntry?.integrity){let actualIntegrity=await getRegistryIntegrity(packageName,installedEntry.version);if(!actualIntegrity||!compareIntegrity(catalogEntry.integrity,actualIntegrity)){try{await pluginManager.remove(packageName,deps.hostServices)}catch(rollbackErr){console.warn(`[plugin-mgr] integrity rollback uninstall failed for ${packageName}:`,rollbackErr)}return getDaemonProfile().audit.emit("agent","plugin.install.rejected",{packageName,version:installedEntry.version,reason:"integrity_mismatch",expected:catalogEntry.integrity,actual:actualIntegrity}),set.status=400,{error:"integrity mismatch",expected:catalogEntry.integrity,actual:actualIntegrity}}}else console.warn(`[plugin-mgr] plugin ${packageName} has no pinned integrity \u2014 accepting npm registry's hash`);let newPlugin=pluginManager.getAllPlugins().find((p)=>p.name===installedEntry.pluginName);if(newPlugin){let lifecycleErrors=[];if(deps.mountPlugin)deps.mountPlugin(newPlugin);if(deps.hostServices){if(alreadyRunning&&existingPlugin?.onServerStop)try{await existingPlugin.onServerStop()}catch(err){lifecycleErrors.push(`onServerStop failed: ${err instanceof Error?err.message:String(err)}`)}if(pluginManager.registerProviders(newPlugin,deps.hostServices),newPlugin.onServerStart&&deps.app)try{await newPlugin.onServerStart(deps.app,deps.hostServices),pluginManager.registerProviders(newPlugin,deps.hostServices)}catch(err){lifecycleErrors.push(`onServerStart failed: ${err instanceof Error?err.message:String(err)}`)}if(newPlugin.onServerReady&&deps.app)try{await newPlugin.onServerReady(deps.app,deps.hostServices)}catch(err){lifecycleErrors.push(`onServerReady failed: ${err instanceof Error?err.message:String(err)}`)}}if(deps.rebuildPluginSurfaces?.(),lifecycleErrors.length>0)return set.status=500,{success:!1,message:`Plugin ${packageName} installed but failed to start`,lifecycleErrors,restartRequired:!0}}let prereqs;if(newPlugin&&deps.app)try{prereqs=await runPluginPrereqs(newPlugin,packageName,deps.app,deps.db,{skipInstall:!!body.skipPrereqs})}catch(err){prereqs=void 0,console.warn(`[plugin-mgr] prereq run failed for ${packageName}:`,err)}return notifyBackendPluginChange(deps.db),getDaemonProfile().audit.emit("agent","plugin.installed",{packageName,version:installedEntry.version,pluginName:installedEntry.pluginName,wasRunning:alreadyRunning}),{success:!0,message:`Plugin ${packageName} installed and loaded successfully`,prereqs:prereqs?{satisfied:prereqs.satisfied,missing:prereqs.status?.missing??[],pendingSudo:prereqs.pendingSudo,skipped:!!body.skipPrereqs}:void 0}})}catch(err){return set.status=500,{error:"Failed to install plugin",details:String(err)}}},{body:t.Object({packageName:t.String(),skipPrereqs:t.Optional(t.Boolean())})}).post("/prereqs/status",async({body,set})=>{if(!body.packageName)return set.status=400,{error:"Missing packageName"};let pkg=body.packageName,plugin=pluginManager.getAllPlugins().find((p)=>p.name===pkg||pluginManager.getLoaded(pkg)?.name===p.name);if(!plugin||!deps.app)return await readPrereqState(deps.db,pkg)??{satisfied:!0,missing:[]};try{let r=await runPluginPrereqs(plugin,pkg,deps.app,deps.db,{skipInstall:!0});return{satisfied:r.satisfied,missing:r.status?.missing??[],noProtocol:r.noProtocol}}catch(err){return set.status=500,{error:String(err)}}},{body:t.Object({packageName:t.String()})}).post("/prereqs/install",async({body,set})=>{if(!body.packageName)return set.status=400,{error:"Missing packageName"};let pkg=body.packageName,plugin=pluginManager.getAllPlugins().find((p)=>p.name===pkg||pluginManager.getLoaded(pkg)?.name===p.name);if(!plugin||!deps.app)return set.status=404,{error:"Plugin not loaded"};try{let r=await runPluginPrereqs(plugin,pkg,deps.app,deps.db,{skipInstall:!1});return{satisfied:r.satisfied,installed:r.install?.installed??[],pendingSudo:r.pendingSudo,errors:r.install?.errors??[],noProtocol:r.noProtocol}}catch(err){return set.status=500,{error:String(err)}}},{body:t.Object({packageName:t.String(),approveSudo:t.Optional(t.Boolean())})}).post("/prereqs/uninstall",async({body,set})=>{if(!body.packageName)return set.status=400,{error:"Missing packageName"};let pkg=body.packageName,plugin=pluginManager.getAllPlugins().find((p)=>p.name===pkg);if(!plugin||!plugin.apiPrefix||!deps.app)return set.status=404,{error:"Plugin not loaded"};let handle=deps.app.handle;if(typeof handle!=="function")return set.status=500,{error:"App handle unavailable"};let path=`${plugin.apiPrefix.replace(/\/+$/,"")}/prereqs/uninstall`,res=await handle.call(deps.app,new Request(`http://agent.local${path}`,{method:"POST",headers:{"x-vc-profile-rewrite":"1",[INTERNAL_DISPATCH_HEADER]:getInternalDispatchToken()}}));if(res.status===404)return set.status=404,{error:"Plugin has no prereqs/uninstall endpoint"};return res.json()},{body:t.Object({packageName:t.String()})}).post("/nuke",async({body,set})=>{if(!deps.hostServices)return set.status=503,{error:"hostServices unavailable"};let agentDir=deps.hostServices.getDataDir?.();return{ok:!0,...await pluginManager.runPluginNuke(body.packageName,deps.hostServices,{agentDir,dryRun:!!body.dryRun})}},{body:t.Object({packageName:t.Optional(t.String()),dryRun:t.Optional(t.Boolean())})}).post("/remove",async({body,set})=>{if(!body.packageName||typeof body.packageName!=="string")return set.status=400,{error:"Missing required field: packageName"};if(body.packageName.startsWith("@burdenoff/"))body.packageName=body.packageName.replace(/^@burdenoff\//,"@vibecontrols/");if(isCriticalPlugin(body.packageName))return{success:!0,message:`Plugin ${body.packageName} is critical and cannot be removed; treating remove as a no-op.`,skipped:!0};try{let packageName=validatePluginPackageName(body.packageName);return await withPluginLifecycleLock(packageName,async()=>{return await pluginManager.remove(packageName,deps.hostServices),deps.rebuildPluginSurfaces?.(),notifyBackendPluginChange(deps.db),getDaemonProfile().audit.emit("agent","plugin.removed",{packageName}),{success:!0,message:`Plugin ${packageName} removed successfully`}})}catch(err){return set.status=500,{error:"Failed to remove plugin",details:String(err)}}},{body:t.Object({packageName:t.String()})}).post("/update",async({body,set})=>{if(!body.packageName||typeof body.packageName!=="string")return set.status=400,{error:"Missing required field: packageName"};try{let packageName=validatePluginPackageName(body.packageName);return await withPluginLifecycleLock(packageName,async()=>{let existingPlugin=pluginManager.getLoaded(packageName);if(existingPlugin?.onServerStop)await existingPlugin.onServerStop({reason:"reload"});if(existingPlugin&&deps.hostServices)deps.hostServices.serviceRegistry.unregisterPlugin(existingPlugin.name);let entry=await pluginManager.update(packageName),updatedPlugin=pluginManager.getLoaded(packageName);if(updatedPlugin&&deps.hostServices){if(pluginManager.registerProviders(updatedPlugin,deps.hostServices),updatedPlugin.onServerStart&&deps.app)await updatedPlugin.onServerStart(deps.app,deps.hostServices),pluginManager.registerProviders(updatedPlugin,deps.hostServices);if(updatedPlugin.onServerReady&&deps.app)await updatedPlugin.onServerReady(deps.app,deps.hostServices)}return deps.rebuildPluginSurfaces?.(),notifyBackendPluginChange(deps.db),{success:!0,message:`Plugin ${packageName} updated to v${entry.version}`,version:entry.version}})}catch(err){return set.status=500,{error:"Failed to update plugin",details:String(err)}}},{body:t.Object({packageName:t.String()})}).post("/update-all",async({set})=>{try{let results=await withPluginLifecycleLock("*",()=>pluginManager.updateAll());return{success:!0,results,message:`Updated ${results.filter((r)=>r.success).length}/${results.length} plugin(s)`}}catch(err){return set.status=500,{error:"Failed to update all plugins",details:String(err)}}}).post("/remove-all",async({set})=>{try{let results=await withPluginLifecycleLock("*",async()=>{let removed=await pluginManager.removeAll(deps.hostServices);return deps.rebuildPluginSurfaces?.(),removed});return{success:!0,results,message:`Removed ${results.filter((r)=>r.success).length}/${results.length} plugin(s)`}}catch(err){return set.status=500,{error:"Failed to remove all plugins",details:String(err)}}}).post("/reload",async({set})=>{try{if(!deps.app||!deps.hostServices)return set.status=503,{error:"Plugin lifecycle services are not ready"};let plugins=await withPluginLifecycleLock("*",async()=>{return await pluginManager.reloadAll(deps.app,deps.hostServices),deps.rebuildPluginSurfaces?.(),pluginManager.getPluginDetails()});return{success:!0,plugins,message:`Reloaded ${plugins.length} plugin(s)`}}catch(err){return set.status=500,{error:"Failed to reload plugins",details:String(err)}}}).post("/:name/reload",async({params,set})=>{if(!deps.app||!deps.hostServices)return set.status=503,{error:"Plugin lifecycle services are not ready"};let target=params.name,plugin=pluginManager.getAllPlugins().find((p)=>p.name===target);if(!plugin)return set.status=404,{error:`Plugin "${target}" not found`};let packageName=pluginManager.getPluginDetails().find((d)=>d.pluginName===target)?.packageName??`core:${target}`;if(isPackageInstallInFlight(packageName))return set.status=409,{error:"Plugin lifecycle in progress; try again shortly",packageName};try{return await withPluginLifecycleLock(packageName,async()=>{if(plugin.onServerStop)try{await plugin.onServerStop({reason:"reload"})}catch(err){console.warn(`[plugin-mgr] onServerStop during reload of ${target}:`,err)}if(deps.hostServices)deps.hostServices.serviceRegistry.unregisterPlugin(plugin.name);if(pluginManager.registerProviders(plugin,deps.hostServices),plugin.onServerStart)await plugin.onServerStart(deps.app,deps.hostServices),pluginManager.registerProviders(plugin,deps.hostServices);if(plugin.onServerReady)await plugin.onServerReady(deps.app,deps.hostServices);return deps.rebuildPluginSurfaces?.(),{success:!0,message:`Plugin ${target} reloaded`,version:plugin.version}})}catch(err){return set.status=500,{error:"Failed to reload plugin",details:String(err)}}},{params:t.Object({name:t.String()})})}import{mkdirSync,writeFileSync,existsSync}from"fs";import{isAbsolute,join,relative,resolve}from"path";var DEFAULT_AGENT_URL="http://localhost:3005",PLUGIN_BASENAME_RE=/^[a-z0-9][a-z0-9-]{0,63}$/,ALLOWED_TAGS=new Set(["backend","frontend","cli","provider","adapter","integration"]);function assertInside(parent,child){let rel=relative(parent,child);if(rel.startsWith("..")||isAbsolute(rel))throw Error("Plugin scaffold path escapes the selected directory.")}function toLiteral(value){return JSON.stringify(value)}function register(program){let cmd=program.command("plugin").description("Manage plugins");cmd.command("list").description("List installed plugins").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON").option("--plain","Force plain text output").action(async(options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged)),fetchData=async()=>{try{return(await apiGet(url,`/api/profiles/${profile}/plugins`)).plugins||[]}catch{warn("Agent not reachable. Attempting local plugin registry...");let{PluginManager}=await import("./plugin-system-5myz4wgw.js");return new PluginManager().getPluginDetails()||[]}},statusOf=(p)=>p.status??(p.loaded===!0||p.enabled===!0?"enabled":"disabled"),nameOf=(p)=>p.pluginName||p.name||p.packageName||p.package||"-";await runMultimode({mode:pickOutputMode(merged),fetchData,plain:(list)=>{if(!list.length){info("No plugins installed.");return}header("Installed Plugins"),formatTable(list.map((p)=>({Name:nameOf(p),Version:p.version||"-",Status:statusOf(p),Description:p.description||"-"})))},interactive:async(list)=>{if(!list.length){header("Installed Plugins"),info("No plugins installed.");return}let rows=list.map((p)=>{let name=nameOf(p),status=statusOf(p),detailLines=[`${colors.bold(name)} ${status==="enabled"?colors.green(status):colors.dim(status)}`,"",` Version: ${p.version||"-"}`,` Description: ${p.description||"-"}`];if(p.tags&&Array.isArray(p.tags))detailLines.push(` Tags: ${p.tags.join(", ")}`);if(p.cliCommand)detailLines.push(` CLI command: vibe ${p.cliCommand}`);return{id:name,label:name,hint:status,detail:detailLines.join(`
|
|
3
|
+
`)}});await interactiveTable({title:`vibe plugin \u2014 ${list.length} installed`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"})},json:(list)=>list.map((p)=>({name:nameOf(p),version:p.version??null,status:statusOf(p),description:p.description??null,tags:p.tags??null,cliCommand:p.cliCommand??null}))})}catch(err){fail(errMsg(err))}}),cmd.command("install").description("Install a plugin").argument("<package>","NPM package name to install").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--skip-prereqs","Skip running the plugin's prereqs/install",!1).option("--json","Emit JSON result").action(async(packageName,options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged));try{let result=await apiPost(url,`/api/profiles/${profile}/plugins/install`,{packageName,skipPrereqs:!!options.skipPrereqs});if(maybePrintJson(merged,{ok:!0,action:"install",package:packageName,version:result?.version??null,prereqs:result?.prereqs??null}))return;if(success(`Plugin "${packageName}" installed.`),result?.version)kv("Version",result.version);if(result?.prereqs){let p=result.prereqs;if(p.skipped)warn(`Prerequisite install skipped. Run \`vibe plugin prereqs ${packageName} install\` later.`);else if(!p.satisfied)warn(`Prerequisites not yet satisfied: ${(p.missing||[]).map((m)=>m.name).join(", ")||"(unknown)"}`);if((p.pendingSudo||[]).length>0){warn(`${p.pendingSudo.length} command(s) need sudo. Run them and re-check with \`vibe plugin prereqs ${packageName} status\`:`);for(let ps of p.pendingSudo)kv(` ${ps.name}`,ps.command)}}}catch{warn("Agent not reachable. Attempting local install...");try{let{PluginManager}=await import("./plugin-system-5myz4wgw.js");if(await new PluginManager().install(packageName),maybePrintJson(merged,{ok:!0,action:"install",package:packageName,local:!0}))return;success(`Plugin "${packageName}" installed locally.`)}catch(localErr){fail(`Could not install plugin via agent or locally: ${errMsg(localErr)}`)}}}catch(err){fail(errMsg(err))}}),cmd.command("remove").description("Remove a plugin").argument("<package>","NPM package name to remove").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON result").action(async(packageName,options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged));try{if(await apiPost(url,`/api/profiles/${profile}/plugins/remove`,{packageName}),maybePrintJson(merged,{ok:!0,action:"remove",package:packageName}))return;success(`Plugin "${packageName}" removed.`)}catch{warn("Agent not reachable. Attempting local removal...");try{let{PluginManager}=await import("./plugin-system-5myz4wgw.js");if(await new PluginManager().remove(packageName),maybePrintJson(merged,{ok:!0,action:"remove",package:packageName,local:!0}))return;success(`Plugin "${packageName}" removed locally.`)}catch(localErr){fail(`Could not remove plugin via agent or locally: ${errMsg(localErr)}`)}}}catch(err){fail(errMsg(err))}}),cmd.command("update").description("Update a plugin (or all plugins with --all)").argument("[package]","NPM package name to update").option("--all","Update all installed plugins",!1).option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON result").action(async(packageName,options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged));if(options.all){info("Updating all plugins...");try{let result=await apiPost(url,`/api/profiles/${profile}/plugins/update-all`,{}),results=result?.results||[];if(maybePrintJson(merged,{ok:!0,action:"update-all",results}))return;for(let r of results)if(r.success)success(`Updated ${r.packageName}${r.version?` to v${r.version}`:""}`);else warn(`Failed to update ${r.packageName}: ${r.error}`);success(result?.message||"Done.")}catch{warn("Agent not reachable. Attempting local update...");try{let{PluginManager}=await import("./plugin-system-5myz4wgw.js"),results=await new PluginManager().updateAll();if(maybePrintJson(merged,{ok:!0,action:"update-all",results,local:!0}))return;for(let r of results)if(r.success)success(`Updated ${r.packageName}${r.version?` to v${r.version}`:""}`);else warn(`Failed to update ${r.packageName}: ${r.error}`)}catch(localErr){fail(`Could not update plugins: ${errMsg(localErr)}`)}}}else if(packageName){info(`Updating ${packageName}...`);try{let result=await apiPost(url,`/api/profiles/${profile}/plugins/update`,{packageName});if(maybePrintJson(merged,{ok:!0,action:"update",package:packageName,version:result?.version??null,message:result?.message??null}))return;success(result?.message||`Plugin "${packageName}" updated.`)}catch{warn("Agent not reachable. Attempting local update...");try{let{PluginManager}=await import("./plugin-system-5myz4wgw.js"),entry=await new PluginManager().update(packageName);if(maybePrintJson(merged,{ok:!0,action:"update",package:packageName,version:entry.version,local:!0}))return;success(`Plugin "${packageName}" updated to v${entry.version}.`)}catch(localErr){fail(`Could not update plugin: ${errMsg(localErr)}`)}}}else fail("Provide a package name to update, or use --all to update all plugins.")}catch(err){fail(errMsg(err))}}),cmd.command("uninstall").description("Uninstall a plugin (or all plugins with --all)").argument("[package]","NPM package name to uninstall").option("--all","Uninstall all external plugins",!1).option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON result").action(async(packageName,options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged));if(options.all){info("Uninstalling all plugins...");try{let result=await apiPost(url,`/api/profiles/${profile}/plugins/remove-all`,{}),results=result?.results||[];if(maybePrintJson(merged,{ok:!0,action:"uninstall-all",results}))return;for(let r of results)if(r.success)success(`Removed ${r.packageName}`);else warn(`Failed to remove ${r.packageName}: ${r.error}`);success(result?.message||"Done.")}catch{warn("Agent not reachable. Attempting local removal...");try{let{PluginManager}=await import("./plugin-system-5myz4wgw.js"),results=await new PluginManager().removeAll();if(maybePrintJson(merged,{ok:!0,action:"uninstall-all",results,local:!0}))return;for(let r of results)if(r.success)success(`Removed ${r.packageName}`);else warn(`Failed to remove ${r.packageName}: ${r.error}`)}catch(localErr){fail(`Could not uninstall plugins: ${errMsg(localErr)}`)}}}else if(packageName)try{if(await apiPost(url,`/api/profiles/${profile}/plugins/remove`,{packageName}),maybePrintJson(merged,{ok:!0,action:"uninstall",package:packageName}))return;success(`Plugin "${packageName}" uninstalled.`)}catch{warn("Agent not reachable. Attempting local removal...");try{let{PluginManager}=await import("./plugin-system-5myz4wgw.js");if(await new PluginManager().remove(packageName),maybePrintJson(merged,{ok:!0,action:"uninstall",package:packageName,local:!0}))return;success(`Plugin "${packageName}" uninstalled locally.`)}catch(localErr){fail(`Could not uninstall plugin: ${errMsg(localErr)}`)}}else fail("Provide a package name to uninstall, or use --all to uninstall all plugins.")}catch(err){fail(errMsg(err))}}),cmd.command("reload").description("Reload all plugins").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON result").action(async(options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged));if(await apiPost(url,`/api/profiles/${profile}/plugins/reload`,{}),maybePrintJson(merged,{ok:!0,action:"reload"}))return;success("Plugins reloaded.")}catch(err){fail(errMsg(err))}}),cmd.command("available").description("Show available plugins").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON").option("--plain","Force plain text output").action(async(options)=>{try{let merged={...program.opts(),...options},url=getAgentUrl(options),profile=encodeURIComponent(resolveProfileName(merged)),fetchData=async()=>{return(await apiGet(url,`/api/profiles/${profile}/plugins/available`)).plugins||[]},nameOf=(p)=>p.name||p.package||"-",versionOf=(p)=>p.version||p.latestVersion||"-";await runMultimode({mode:pickOutputMode(merged),fetchData,plain:(plugins)=>{if(!plugins||plugins.length===0){info("No available plugins found.");return}header("Available Plugins"),formatTable(plugins.map((p)=>({Name:nameOf(p),Version:versionOf(p),Description:p.description||"-"})))},interactive:async(plugins)=>{if(!plugins||plugins.length===0){header("Available Plugins"),info("No available plugins found.");return}let rows=plugins.map((p)=>{let name=nameOf(p),lines=[colors.bold(name),"",` Version: ${versionOf(p)}`,` Description: ${p.description||"-"}`];return{id:name,label:name,hint:versionOf(p),detail:lines.join(`
|
|
4
4
|
`)}});await interactiveTable({title:`vibe plugin available \u2014 ${plugins.length} plugin(s)`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"})},json:(plugins)=>plugins.map((p)=>({name:nameOf(p),version:versionOf(p),description:p.description??null}))})}catch(err){fail(errMsg(err))}});let prereqsCmd=cmd.command("prereqs").description("Inspect or run a plugin's prerequisite-install protocol").argument("<package>","NPM package name (or short plugin name)");prereqsCmd.command("status",{isDefault:!0}).description("Show the plugin's prereq status").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON").option("--plain","Force plain text output").action(async(options,cmdRef)=>{let pkg=cmdRef.parent?.args?.[0];if(!pkg){fail("Missing plugin package argument.");return}try{let merged={...program.opts(),...options},url=getAgentUrl(options),fetchData=async()=>apiPost(url,"/api/plugins/prereqs/status",{packageName:pkg});await runMultimode({mode:pickOutputMode(merged),fetchData,plain:(r)=>{if(kv("Plugin",pkg),kv("Satisfied",r?.satisfied?"yes":"no"),Array.isArray(r?.missing)&&r.missing.length>0)kv("Missing",r.missing.map((m)=>m.name).join(", "))},interactive:async(r)=>{let lines=[`Plugin: ${pkg}`,`Satisfied: ${r?.satisfied?"yes":"no"}`];if(Array.isArray(r?.missing)&&r.missing.length>0)lines.push(`Missing: ${r.missing.map((m)=>m.name).join(", ")}`);await interactiveDetail({title:`Prereqs: ${pkg}`,body:lines.join(`
|
|
5
5
|
`),footer:"q / Esc to quit"})},json:(r)=>({plugin:pkg,satisfied:!!r?.satisfied,missing:Array.isArray(r?.missing)?r.missing:[],pendingSudo:Array.isArray(r?.pendingSudo)?r.pendingSudo:[]})})}catch(err){fail(`Could not read prereq status: ${errMsg(err)}`)}}),prereqsCmd.command("install").description("Run the plugin's prerequisite install endpoint").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--approve-sudo","Auto-approve sudo commands the plugin returns",!1).option("--json","Emit JSON result").action(async(options,cmdRef)=>{let pkg=cmdRef.parent?.args?.[0];if(!pkg){fail("Missing plugin package argument.");return}try{let merged={...program.opts(),...options},url=getAgentUrl(options),r=await apiPost(url,"/api/plugins/prereqs/install",{packageName:pkg,approveSudo:!!options.approveSudo});if(maybePrintJson(merged,{ok:!0,action:"prereqs-install",plugin:pkg,satisfied:!!r?.satisfied,pendingSudo:r?.pendingSudo??[]}))return;if(r?.satisfied)success(`Prerequisites satisfied for ${pkg}.`);else warn(`Prerequisites still missing for ${pkg}.`);if(Array.isArray(r?.pendingSudo)&&r.pendingSudo.length>0){warn(`${r.pendingSudo.length} command(s) require sudo:`);for(let ps of r.pendingSudo)kv(` ${ps.name}`,ps.command)}}catch(err){fail(`Prereq install failed: ${errMsg(err)}`)}}),prereqsCmd.command("uninstall").description("Run the plugin's prerequisite uninstall endpoint (if supported)").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON result").action(async(options,cmdRef)=>{let pkg=cmdRef.parent?.args?.[0];if(!pkg){fail("Missing plugin package argument.");return}try{let merged={...program.opts(),...options},url=getAgentUrl(options);if(await apiPost(url,"/api/plugins/prereqs/uninstall",{packageName:pkg}),maybePrintJson(merged,{ok:!0,action:"prereqs-uninstall",plugin:pkg}))return;success(`Prereq uninstall completed for ${pkg}.`)}catch(err){warn(`Prereq uninstall not supported or failed: ${errMsg(err)}`)}}),cmd.command("create").description("Scaffold a new plugin project").argument("<name>",'Plugin name (e.g. "docker" \u2192 vibe-plugin-docker)').option("-d, --dir <directory>","Parent directory",".").option("--tag <tag>","Plugin tag (backend, frontend, cli, provider, adapter, integration)","backend").option("--with-ui","Include a companion UI plugin scaffold",!1).option("--json","Emit JSON result").action(async(name,options)=>{try{let merged={...program.opts(),...options},baseName=name.startsWith("vibe-plugin-")?name.slice(12):name;if(!PLUGIN_BASENAME_RE.test(baseName)){fail("Plugin name must use lowercase letters, numbers, and dashes only.");return}let tag=options.tag;if(!ALLOWED_TAGS.has(tag)){fail("Invalid plugin tag.");return}let pluginName=`vibe-plugin-${baseName}`,parentDir=resolve(options.dir),dir=resolve(parentDir,pluginName);if(assertInside(parentDir,dir),existsSync(dir)){fail(`Directory "${dir}" already exists.`);return}info(`Scaffolding ${pluginName}...`),mkdirSync(join(dir,"src"),{recursive:!0}),mkdirSync(join(dir,"dist"),{recursive:!0});let description=`VibeControls plugin: ${baseName}`,packageJson={name:`@vibecontrols/${pluginName}`,version:"0.1.0",main:"./dist/index.js",type:"module",engines:{bun:">=1.3.0"},scripts:{build:"bun build ./src/index.ts --outdir ./dist --target bun",lint:"eslint ./src",format:"bunx prettier . --write","format:check":"bunx prettier . --check","type:check":"tsc --noEmit",clean:"rimraf dist",prebuild:"bun run clean",prepublishOnly:"bun run build",sanity:"bun run format:check && bun run lint && bun run type:check && bun run build"},keywords:["vibecontrols","vibe","vibe-plugin",baseName,"bun"],author:{name:"Your Name",email:"you@example.com"},license:"SEE LICENSE IN LICENSE",description,devDependencies:{"@types/bun":"^1.2.16","bun-types":"^1.3.9",commander:"^14.0.3",eslint:"^9.30.1",prettier:"^3.6.2",rimraf:"^6.0.1",typescript:"^5.8.3","typescript-eslint":"^8.56.0"},peerDependencies:{"@vibecontrols/agent":">=2.0.0"},peerDependenciesMeta:{"@vibecontrols/agent":{optional:!0}},publishConfig:{access:"public",registry:"https://registry.npmjs.org/"},files:["dist/**/*","README.md","LICENSE"]};writeFileSync(join(dir,"package.json"),JSON.stringify(packageJson,null,2)+`
|
|
6
6
|
`);let tsconfig={compilerOptions:{target:"ES2022",module:"ES2022",moduleResolution:"bundler",types:["bun-types"],strict:!0,esModuleInterop:!0,skipLibCheck:!0,outDir:"./dist",rootDir:"./src",declaration:!0,sourceMap:!0},include:["src/**/*.ts"],exclude:["node_modules","dist"]};writeFileSync(join(dir,"tsconfig.json"),JSON.stringify(tsconfig,null,2)+`
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
import{createApp,invalidatePairingToken,mintPairingToken}from"./index-
|
|
3
|
+
import{createApp,invalidatePairingToken,mintPairingToken}from"./index-her974n1.js";import"./index-926vt87h.js";import{bootstrapWorkspace}from"./index-ns2dd8nx.js";import"./index-g9h64dn9.js";import"./index-skmkfyzb.js";import"./index-95nmejfd.js";import"./index-34syaqa8.js";import"./index-y5q0m3cx.js";import"./index-n3y918j2.js";import"./index-h6a1kkfk.js";import"./index-srbb2214.js";import{telemetryService}from"./index-sfjbh6fa.js";import"./index-3jez1q8j.js";import"./index-brtw3j8x.js";import{getAgentApiKey,loadAgentApiKey}from"./index-b4wy3jrt.js";import"./index-d3mz9vws.js";import"./index-rnk0kny8.js";import{applyAgentConfigToEnv,applyAgentSecretsToEnv,getSecret,readAgentConfig,setSecret}from"./index-2pqv0bya.js";import{getDaemonProfile}from"./index-7pdmqbj8.js";import{getProfileDataDir,getVibecontrolsDir,getVibecontrolsProductDir,getVibecontrolsProfile}from"./index-bysm7taq.js";import"./index-zs58d1hc.js";import{__require}from"./index-e9rt4m94.js";import{isAbsolute}from"path";import{existsSync,mkdirSync,readFileSync,readdirSync,renameSync,unlinkSync,writeFileSync}from"fs";import{dirname,join}from"path";var MIGRATION_MARKER=".secrets-migrated-v1",SECRET_KEYS=["static-api-key","clientId","clientSecret"];function composeName(scope,key){return`${scope}:${key}`}function getMarkerPath(){return join(getVibecontrolsProductDir(),MIGRATION_MARKER)}function writeMarker(){let path=getMarkerPath();mkdirSync(dirname(path),{recursive:!0}),writeFileSync(path,`${new Date().toISOString()}
|
|
4
4
|
`,{mode:384})}function parseJsonFile(path){if(!existsSync(path))return null;try{let raw=readFileSync(path,"utf8"),parsed=JSON.parse(raw);if(parsed&&typeof parsed==="object"&&!Array.isArray(parsed))return parsed}catch{}return null}function atomicWriteJson(path,data){mkdirSync(dirname(path),{recursive:!0});let tmp=`${path}.${process.pid}.tmp`;writeFileSync(tmp,`${JSON.stringify(data,null,2)}
|
|
5
5
|
`,{mode:384}),renameSync(tmp,path)}function listAgentConfigs(){let productDir=getVibecontrolsProductDir(),agentsDir=join(productDir,"agents");if(!existsSync(agentsDir))return[];let entries=[],names;try{names=readdirSync(agentsDir)}catch{return[]}for(let name of names){let cfgPath=join(agentsDir,name,"config.json");if(!existsSync(cfgPath))continue;entries.push({path:cfgPath,scope:name})}return entries}function listProfileFiles(){let productDir=getVibecontrolsProductDir(),profilesDir=join(productDir,"profiles");if(!existsSync(profilesDir))return[];let entries=[],names;try{names=readdirSync(profilesDir)}catch{return[]}for(let name of names){if(!name.endsWith(".json"))continue;entries.push({path:join(profilesDir,name),scope:name.slice(0,-5)})}return entries}async function migrateOneJson(path,scope){let data=parseJsonFile(path);if(!data)return 0;let moved=0;for(let key of SECRET_KEYS){let v=data[key];if(typeof v!=="string"||v.length===0)continue;let composed=composeName(scope,key);try{await setSecret(composed,v),delete data[key],moved++,getDaemonProfile().logger.info("secrets-migrator","migrated field",{path,scope,key})}catch(err){getDaemonProfile().logger.warn("secrets-migrator","failed to migrate field",{path,scope,key,error:String(err)})}}if(moved>0)atomicWriteJson(path,data);return moved}async function runSecretsMigrationOnce(){let result={alreadyMigrated:!1,fieldsMoved:0,filesUpdated:0};if(existsSync(getMarkerPath()))return result.alreadyMigrated=!0,result;let targets=[...listAgentConfigs(),...listProfileFiles()];for(let t of targets){let moved;try{moved=await migrateOneJson(t.path,t.scope)}catch(err){getDaemonProfile().logger.warn("secrets-migrator","skipping target after error",{path:t.path,error:String(err)});continue}if(moved>0)result.fieldsMoved+=moved,result.filesUpdated++}try{writeMarker()}catch(err){getDaemonProfile().logger.warn("secrets-migrator","failed to drop migration marker",{error:String(err)})}if(result.fieldsMoved>0)getDaemonProfile().logger.info("secrets-migrator","migration complete",{fieldsMoved:result.fieldsMoved,filesUpdated:result.filesUpdated});return result}{let originalJsonParse=JSON.parse.bind(JSON);JSON.parse=(text,reviver)=>{try{return originalJsonParse(text,reviver)}catch(err){if(typeof text==="string"&&text.endsWith("}}"))try{return originalJsonParse(text.slice(0,-1),reviver)}catch{}throw err}};let RequestProto=globalThis.Request?.prototype;if(RequestProto&&typeof RequestProto.json==="function"){let originalJson=RequestProto.json;RequestProto.json=async function(){let text=await this.text();if(!text)return;try{return originalJsonParse(text)}catch{if(text.endsWith("}}"))try{return originalJsonParse(text.slice(0,-1))}catch{}let fresh=new Request("http://shim/",{method:"POST",body:text});return originalJson.call(fresh)}}}}bootstrapWorkspace();applyAgentConfigToEnv();if(!process.env.VIBECONTROLS_DATA_DIR)process.env.VIBECONTROLS_DATA_DIR=getVibecontrolsDir();if(typeof Bun>"u")console.error("ERROR: VibeControls Agent requires Bun runtime."),console.error("Install Bun: https://bun.sh"),process.exit(1);function defaultPortForProfile(profile){if(profile==="default")return 3005;let h=2166136261;for(let i=0;i<profile.length;i++)h=Math.imul(h^profile.charCodeAt(i),16777619);return 3100+Math.abs(h)%300}var _resolvedProfile=(()=>{try{return getVibecontrolsProfile()}catch{return"default"}})(),parsedPort=Number.parseInt(process.env.PORT||String(defaultPortForProfile(_resolvedProfile)),10),port=Number.isInteger(parsedPort)&&parsedPort>0?parsedPort:3005,host=process.env.HOST||"0.0.0.0",dbPath=(()=>{let fromEnv=process.env.DB_PATH;if(!fromEnv)return;if(isAbsolute(fromEnv))return fromEnv;console.warn(`[boot] Ignoring relative DB_PATH=${fromEnv} (cwd=${process.cwd()}). Use an absolute path or unset to fall back to ${getProfileDataDir()}.`);return})(),apiKey=process.env.AGENT_API_KEY||void 0,logLevel=process.env.LOG_LEVEL||"info",corsOrigin=process.env.CORS_ORIGIN||void 0;async function main(){try{await runSecretsMigrationOnce()}catch(err){getDaemonProfile().logger.warn("server","secrets migration encountered an error",{error:String(err)})}let legacyJsonKey=readAgentConfig()["static-api-key"],profile=(()=>{try{return getVibecontrolsProfile()}catch{return"default"}})(),preExistingApiKey=legacyJsonKey??await getSecret(`${profile}:static-api-key`),isFirstBoot=!apiKey&&!preExistingApiKey;await loadAgentApiKey(),await applyAgentSecretsToEnv();let vibeClientId=process.env.VIBE_CLIENT_ID,vibeClientSecret=process.env.VIBE_CLIENT_SECRET,vibeGlobalGatewayUrl=process.env.VIBE_GLOBAL_GATEWAY_URL,vibeWorkspaceGatewayUrl=process.env.VIBE_WORKSPACE_GATEWAY_URL,vibeWorkspaceId=process.env.VIBE_WORKSPACE_ID,hasInlineCreds=!!vibeClientId&&!!vibeClientSecret&&!!vibeGlobalGatewayUrl&&!!vibeWorkspaceGatewayUrl&&!!vibeWorkspaceId;getDaemonProfile().logger.info("server","Starting VibeControls Agent...",{runtime:"bun",bunVersion:Bun.version,port,host,env:"production"});try{let cfgRaw=readAgentConfig(),persisted=cfgRaw["telemetry.enabled"]===!0||cfgRaw["telemetry.enabled"]==="true";telemetryService.init({persisted});let startedAt=Date.now();telemetryService.emit("agent.started",{version:process.env.npm_package_version??"unknown",profile:_resolvedProfile,platform:process.platform}),process.__vibe_started_at=startedAt}catch{}let maxBootAttempts=Math.max(1,Number.parseInt(process.env.VIBECONTROLS_AGENT_BOOT_RETRIES??"10",10)||10),isFatalBootError=(err)=>{let msg=String(err?.message??err).toLowerCase();return msg.includes("syntax")||msg.includes("cannot find module")||msg.includes("module not found")||msg.includes("typeerror")||msg.includes("config schema")||msg.includes("invalid config")||msg.includes("corrupt")||msg.includes("eaccess")||msg.includes("eperm")},bootstrap=null,attempt=0;while(!bootstrap){attempt++;try{bootstrap=await createApp({port,host,dbPath,apiKey,logLevel,corsOrigin}),await bootstrap.start();try{let fs=await import("fs"),runtimePath=`${getVibecontrolsDir()}/runtime.json`;fs.writeFileSync(runtimePath,JSON.stringify({pid:process.pid,port,host,profile:_resolvedProfile,startedAt:new Date().toISOString()},null,2));let cleanup=()=>{try{if(fs.existsSync(runtimePath))fs.unlinkSync(runtimePath)}catch{}};process.once("SIGTERM",cleanup),process.once("SIGINT",cleanup),process.once("exit",cleanup)}catch(err){getDaemonProfile().logger.warn("server","Could not write runtime.json",{error:String(err)})}}catch(err){let fatal=isFatalBootError(err);if(getDaemonProfile().logger.error("server","Failed to start agent server",{attempt,maxBootAttempts,fatal,error:String(err)}),fatal||attempt>=maxBootAttempts)console.error(`\u274C Failed to start VibeControls Agent (attempt ${attempt}/${maxBootAttempts}, fatal=${fatal}):`,err),process.exit(1);let delayMs=Math.min(30000,1000*2**Math.min(5,attempt-1)),jitter=Math.floor(Math.random()*500),wait=delayMs+jitter;console.warn(`\u26A0\uFE0F Agent boot attempt ${attempt}/${maxBootAttempts} failed (transient), retrying in ${Math.round(wait/1000)}s...`),await new Promise((r)=>setTimeout(r,wait)),bootstrap=null}}let tunnelProvider=bootstrap.getServiceRegistry().getProvider("tunnel"),tunnelUrl=null;if(tunnelProvider?.getActiveTunnelUrl)for(let i=0;i<30;i++){if(tunnelUrl=await tunnelProvider.getActiveTunnelUrl(),tunnelUrl)break;await new Promise((r)=>setTimeout(r,1000))}if(!tunnelUrl){let{sanitizeEnvSuffix}=await import("./tunnel-bootstrap-x82wcnt8.js"),{getVibecontrolsProfile:getVibecontrolsProfile2}=await import("./path-utils-y2ba2951.js"),profileSuffix=sanitizeEnvSuffix(getVibecontrolsProfile2());tunnelUrl=process.env[`AGENT_TUNNEL_URL_${profileSuffix}`]??process.env.AGENT_TUNNEL_URL??null}let cfg=readAgentConfig(),resolvedApiKey=apiKey||getAgentApiKey()||cfg["static-api-key"]||"",bootState=getDaemonProfile().getBootState(),pairingToken=bootState==="ready"?null:mintPairingToken();if(bootState==="ready")invalidatePairingToken();printBanner({port,host,tunnelUrl,apiKey:resolvedApiKey,revealApiKey:isFirstBoot||process.env.AGENT_PRINT_API_KEY==="1",state:bootState,pairingToken});let{existsSync:existsSync2,unlinkSync:unlinkSync2}=await import("fs"),{tmpdir}=await import("os"),{join:joinPath}=await import("path"),shutdown=async(signal)=>{getDaemonProfile().logger.info("server",`Received ${signal}, shutting down...`);try{let startedAt=process.__vibe_started_at??Date.now();telemetryService.emit("agent.stopped",{uptime_s:Math.round((Date.now()-startedAt)/1000),reason:signal}),await telemetryService.flush(),telemetryService.stop()}catch{}let lifecycle=bootstrap.getLifecycle();if(signal==="SIGTERM"){let flagPath=joinPath(tmpdir(),`.boff-vibecontrols-kill-${process.pid}`);if(existsSync2(flagPath)){try{unlinkSync2(flagPath)}catch{}if(getDaemonProfile().logger.info("server","Kill-intent flag detected \u2014 full shutdown"),lifecycle)await lifecycle.kill();else await bootstrap.stop({reason:"shutdown"}),process.exit(0)}else if(getDaemonProfile().logger.info("server","No kill flag \u2014 treating as hot-reload (soft stop)"),lifecycle)await lifecycle.softStop();else await bootstrap.stop({reason:"reload"})}else if(lifecycle)await lifecycle.kill();else await bootstrap.stop({reason:"shutdown"}),process.exit(0)};if(process.on("SIGINT",()=>shutdown("SIGINT")),process.on("SIGTERM",()=>shutdown("SIGTERM")),hasInlineCreds){let timeoutMs=(()=>{let raw=process.env.VIBECONTROLS_FINALIZE_TIMEOUT_MS,n=raw?Number.parseInt(raw,10):NaN;return Number.isFinite(n)&&n>0?n:300000})();getDaemonProfile().logger.info("server",`VIBE_* env vars detected \u2014 running inline finalize (timeout ${Math.round(timeoutMs/1000)}s)`),(async()=>{let inlineCreds={clientId:vibeClientId,clientSecret:vibeClientSecret,workspaceId:vibeWorkspaceId,globalGatewayUrl:vibeGlobalGatewayUrl,workspaceGatewayUrl:vibeWorkspaceGatewayUrl};try{let result=await Promise.race([bootstrap.finalize(inlineCreds),new Promise((resolve)=>setTimeout(()=>resolve({ok:!1,error:`inline finalize timed out after ${Math.round(timeoutMs/1000)}s`}),timeoutMs))]);if(!result.ok){getDaemonProfile().logger.warn("server","Inline finalize failed \u2014 agent stays awaiting-config. Background retry worker will keep trying.",{error:result.error});let{startFinalizeRetryWorker}=await import("./finalize-retry-worker-3q4xc65x.js"),{setFinalizeRetryHandle}=await import("./finalize-retry-handle-registry-vbq5qhj1.js"),handle=startFinalizeRetryWorker({trigger:()=>bootstrap.finalize(inlineCreds)});setFinalizeRetryHandle(handle)}}catch(err){getDaemonProfile().logger.error("server","Inline finalize threw",{error:String(err)})}})()}else getDaemonProfile().logger.info("server","Agent is awaiting gateway credentials. Add it on the VibeControls platform using the tunnel URL and API key shown above.")}function printBanner(opts){let pad=(s)=>("\u2551 "+s).padEnd(65)+"\u2551",line="\u2560"+"\u2550".repeat(64)+"\u2563",top="\u2554"+"\u2550".repeat(64)+"\u2557",bot="\u255A"+"\u2550".repeat(64)+"\u255D",displayKey=opts.revealApiKey?opts.apiKey:opts.apiKey?`${opts.apiKey.substring(0,8)}****${opts.apiKey.substring(opts.apiKey.length-4)}`:"(none)";if(console.log(""),console.log(top),console.log(pad("VibeControls Agent")),console.log(line),console.log(pad(`State: ${opts.state}`)),console.log(pad(`Agent URL: http://${opts.host}:${opts.port}`)),console.log(pad(`API Key: ${displayKey}`)),console.log(pad(`Tunnel URL: ${opts.tunnelUrl??"(none)"}`)),opts.pairingToken)console.log(pad(`Pairing: ${opts.pairingToken}`)),console.log(pad(" (send as x-pairing-token on bootstrap)"));if(console.log(line),console.log(pad(`Health: http://localhost:${opts.port}/health`)),console.log(pad(`Status: http://localhost:${opts.port}/api/agent/status`)),console.log(bot),!opts.revealApiKey)console.log(" (Set AGENT_PRINT_API_KEY=1 to print the API key in full.)");console.log("")}main();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{CORE_PLUGIN_NAMES,DEFAULT_PLUGINS,PluginManager,TRUSTED_PACKAGES,getPluginPackageRoot,validatePluginPackageName}from"./index-
|
|
2
|
+
import{CORE_PLUGIN_NAMES,DEFAULT_PLUGINS,PluginManager,TRUSTED_PACKAGES,getPluginPackageRoot,validatePluginPackageName}from"./index-h6a1kkfk.js";import"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{validatePluginPackageName,getPluginPackageRoot,TRUSTED_PACKAGES,PluginManager,DEFAULT_PLUGINS,CORE_PLUGIN_NAMES};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{attachSecondaryProfile}from"./index-
|
|
2
|
+
import{attachSecondaryProfile}from"./index-g9h64dn9.js";import"./index-n3y918j2.js";import"./index-h6a1kkfk.js";import"./index-srbb2214.js";import"./index-brtw3j8x.js";import"./index-b4wy3jrt.js";import"./index-d3mz9vws.js";import"./index-rnk0kny8.js";import"./index-2pqv0bya.js";import"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{attachSecondaryProfile};
|