@vibecontrols/agent 2026.531.13 → 2026.531.15
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-nepbpk4d.js +2 -0
- package/dist/cli.js +1 -1
- package/dist/{index-fwd90spw.js → index-0n663tk5.js} +2 -2
- package/dist/{index-pd9haevn.js → index-4s5hv6a7.js} +2 -2
- package/dist/index-7qkzdq23.js +3 -0
- package/dist/{index-1x89a7r1.js → index-cyhcvfxs.js} +2 -2
- package/dist/{index-q96eskyn.js → index-mq3amjcf.js} +1 -1
- package/dist/{index-h2n00ae3.js → index-s3vbcsga.js} +2 -2
- package/dist/{index-ttafzcs7.js → index-wk359257.js} +1 -1
- package/dist/{index-vfwervz9.js → index-wmeb40wr.js} +1 -1
- package/dist/{index-rv8as74y.js → index-yhwmtghn.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{plugin-system-3z2smf2a.js → plugin-system-w3vnn5vh.js} +1 -1
- package/dist/{secondary-profile-attach-9c1rp4y7.js → secondary-profile-attach-mgt3bv4j.js} +1 -1
- package/package.json +1 -1
- package/dist/app-w2196qbz.js +0 -2
- package/dist/index-49a60qjr.js +0 -3
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{createApp}from"./index-s3vbcsga.js";import"./index-926vt87h.js";import"./index-mq3amjcf.js";import"./index-skmkfyzb.js";import"./index-95nmejfd.js";import"./index-wmeb40wr.js";import"./index-y5q0m3cx.js";import"./index-wk359257.js";import"./index-cyhcvfxs.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-vfwervz9.js";import{REDACTED_VALUE,redactUnknownSecrets}from"./index-y5q0m3cx.js";import{CliContributorRegistry,createIframeBridge,getActiveContributorRegistry,setActiveContributorRegistry}from"./index-ttafzcs7.js";import{ABILITIES,PLUGIN_CATALOG,PluginManager,ServiceRegistry,collectPluginBinaries,getDefaultPluginsForAbilities,resolvePluginByShortName}from"./index-1x89a7r1.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-w2196qbz.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-wmeb40wr.js";import{REDACTED_VALUE,redactUnknownSecrets}from"./index-y5q0m3cx.js";import{CliContributorRegistry,createIframeBridge,getActiveContributorRegistry,setActiveContributorRegistry}from"./index-wk359257.js";import{ABILITIES,PLUGIN_CATALOG,PluginManager,ServiceRegistry,collectPluginBinaries,getDefaultPluginsForAbilities,resolvePluginByShortName}from"./index-cyhcvfxs.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-nepbpk4d.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}from"./index-
|
|
2
|
+
import{denyNonLocalMutation}from"./index-7qkzdq23.js";import{assertReadableFileSize,isProtectedPath,isSensitivePath,listDirectoryCapped,resolveSafePath}from"./index-skmkfyzb.js";import{getOsAdapter}from"./index-srbb2214.js";import{Elysia,t}from"./index-rnk0kny8.js";import{apiGet,apiPost,getAgentUrl,isLocalAgentUrl,maybePrintJson,pickOutputMode,resolveProfileName,runMultimode}from"./index-wvabz3xh.js";import{errMsg,fail,formatStatus,formatTable,header,info,shortId,success,timeAgo}from"./index-r0qezdp7.js";import{interactiveTable}from"./index-campp0wv.js";import"./index-2pqv0bya.js";import{gatewayClient,isAllowedCallbackUrl}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";import{promises as fs}from"fs";import path from"path";import os from"os";function positiveIntFromEnv(name,fallback){let parsed=Number.parseInt(process.env[name]??"",10);return Number.isInteger(parsed)&&parsed>0?parsed:fallback}var MAX_CONCURRENT_TASKS=positiveIntFromEnv("VIBECONTROLS_MAX_CONCURRENT_TASKS",4),MAX_TASK_OUTPUT_BYTES=positiveIntFromEnv("VIBECONTROLS_MAX_TASK_OUTPUT_BYTES",1048576),ALLOWED_INTERPRETERS=new Set(["bash","sh","zsh","python","python3","node","bun","powershell","pwsh","cmd"]);async function executeCommand(payload,timeout){if(!payload.command)throw Error("Command is required");let cwd=payload.cwd?(await resolveSafePath(payload.cwd,{mustExist:!0})).path:process.cwd(),proc=Bun.spawn(getOsAdapter().shellArgv(payload.command),{cwd,env:{...process.env,...payload.env},stdout:"pipe",stderr:"pipe"}),killed=!1,timeoutId;if(timeout&&timeout>0)timeoutId=setTimeout(()=>{killed=!0,proc.kill()},timeout);let[stdout,stderr]=await Promise.all([readProcessStream(proc.stdout,()=>proc.kill()),readProcessStream(proc.stderr,()=>proc.kill())]),exitCode=await proc.exited;if(timeoutId)clearTimeout(timeoutId);if(killed)throw Error(`Command timed out after ${timeout}ms`);return{stdout,stderr,exitCode}}async function executeScript(payload,timeout){if(!payload.script)throw Error("Script is required");let isWin=getOsAdapter().platform==="win32",interpreter=payload.interpreter||(isWin?"powershell":"bash");if(!ALLOWED_INTERPRETERS.has(interpreter)||interpreter.includes("/"))throw Error(`Interpreter '${interpreter}' is not allowed`);let cwd=payload.cwd?(await resolveSafePath(payload.cwd,{mustExist:!0})).path:process.cwd(),tmpDir=os.tmpdir(),scriptExt=interpreter==="powershell"||interpreter==="pwsh"?"ps1":interpreter==="cmd"?"cmd":"sh",scriptFile=path.join(tmpDir,`vibecontrols-script-${globalThis.crypto.randomUUID()}.${scriptExt}`);await fs.writeFile(scriptFile,payload.script,{mode:448});try{let env=payload.env?{...process.env,...payload.env}:process.env,argv=interpreter==="powershell"||interpreter==="pwsh"?[interpreter,"-NoProfile","-ExecutionPolicy","Bypass","-File",scriptFile]:interpreter==="cmd"?["cmd","/c",scriptFile]:[interpreter,scriptFile],proc=Bun.spawn(argv,{cwd,env,stdout:"pipe",stderr:"pipe"}),killed=!1,timeoutId;if(timeout&&timeout>0)timeoutId=setTimeout(()=>{killed=!0,proc.kill()},timeout);let[stdout,stderr]=await Promise.all([readProcessStream(proc.stdout,()=>proc.kill()),readProcessStream(proc.stderr,()=>proc.kill())]),exitCode=await proc.exited;if(timeoutId)clearTimeout(timeoutId);if(killed)throw Error(`Script timed out after ${timeout}ms`);return{stdout,stderr,exitCode}}finally{await fs.unlink(scriptFile).catch(()=>{})}}async function readProcessStream(stream,onLimit){let decoder=new TextDecoder,output="",total=0,limited=!1;for await(let chunk of stream){if(total+=chunk.byteLength,total>MAX_TASK_OUTPUT_BYTES){if(!limited)limited=!0,onLimit(),output+=`
|
|
3
3
|
[output truncated: task output limit exceeded]
|
|
4
|
-
`;continue}output+=decoder.decode(chunk,{stream:!0})}return output+=decoder.decode(),output}async function executeFileOperation(payload){if(!payload.path)throw Error("Path is required");switch(payload.operation){case"read":{let safePath=(await resolveSafePath(payload.path,{mustExist:!0})).path;if(isSensitivePath(safePath))throw Error("Access to sensitive files not allowed");return await assertReadableFileSize(safePath),{content:await fs.readFile(safePath,"utf-8"),path:safePath}}case"write":{if(!payload.content)throw Error("Content is required for write operation");let safePath=(await resolveSafePath(payload.path,{forWrite:!0})).path;if(isProtectedPath(safePath)||isSensitivePath(safePath))throw Error("Write access to sensitive files not allowed");return await fs.writeFile(safePath,payload.content),{success:!0,path:safePath}}case"delete":{let safePath=(await resolveSafePath(payload.path,{mustExist:!0})).path;if(isProtectedPath(safePath)||isSensitivePath(safePath))throw Error("Delete access to this path is not allowed");return await fs.unlink(safePath),{success:!0,path:safePath}}case"exists":try{let safePath=(await resolveSafePath(payload.path)).path;return await fs.access(safePath),{exists:!0,path:safePath}}catch{return{exists:!1}}case"list":{let safePath=(await resolveSafePath(payload.path,{mustExist:!0})).path;return{files:(await listDirectoryCapped(safePath)).map((entry)=>entry.name),path:safePath}}default:throw Error(`Unknown file operation: ${payload.operation}`)}}async function processTask(db,task,concurrency,options){try{let current=await db.getTask(task.id);if(current&¤t.status!=="pending")return;await db.updateTask(task.id,{status:"running"});let payload=JSON.parse(task.payload),result,exitCode;switch(task.type){case"command":{let cmdResult=await executeCommand(payload,options?.timeout);result=cmdResult,exitCode=cmdResult.exitCode;break}case"script":{let scriptResult=await executeScript(payload,options?.timeout);result=scriptResult,exitCode=scriptResult.exitCode;break}case"file_operation":result=await executeFileOperation(payload);break;default:throw Error(`Unknown task type: ${task.type}`)}let isFailedExit=exitCode!==void 0&&exitCode!==0,finalStatus=isFailedExit?"failed":"completed";if(await db.updateTask(task.id,{status:finalStatus,result:JSON.stringify(result),...exitCode!==void 0&&{exitCode},...isFailedExit&&{error:`Process exited with code ${exitCode}`}}),options?.callbackUrl)sendCallback(options.callbackUrl,{agentTaskId:task.id,status:finalStatus,result,exitCode,...isFailedExit&&{error:`Process exited with code ${exitCode}`}})}catch(err){let errorMsg=err instanceof Error?err.message:String(err);try{await db.updateTask(task.id,{status:"failed",error:errorMsg})}catch(updateErr){console.warn(`[task] failed to mark task ${task.id} as failed: ${updateErr instanceof Error?updateErr.message:String(updateErr)}`)}if(options?.callbackUrl)sendCallback(options.callbackUrl,{agentTaskId:task.id,status:"failed",error:errorMsg})}finally{concurrency.active=Math.max(0,concurrency.active-1)}}function sendCallback(url,body){fetch(url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body),signal:AbortSignal.timeout(1e4)}).catch(()=>{})}function createRoutes(deps){let{db}=deps,concurrency={active:0};return new Elysia().get("/",async({query})=>{if(query.status==="pending")return{tasks:await db.getPendingTasks()};return{tasks:await db.getAllTasks()}}).get("/:id",async({params,set})=>{let task=await db.getTask(params.id);if(!task)return set.status=404,{error:"Task not found"};return{task}}).post("/legacy",async({body,set})=>{try{return{task:await db.createTask({id:globalThis.crypto.randomUUID(),type:"file_operation",status:"completed",payload:JSON.stringify({operation:"exists",path:process.cwd(),meta:body}),result:JSON.stringify({legacy:!0,note:"Legacy doctor-script task accepted (no-op)."})})}}catch(err){return set.status=500,{error:"Failed to create task",details:String(err)}}},{body:t.Any()}).post("/",async({body,request,server,set})=>{let legacy=body;if(legacy&&typeof legacy==="object"&&!legacy.type&&!legacy.payload&&(legacy.title||legacy.description||legacy.command))try{return{task:await db.createTask({id:globalThis.crypto.randomUUID(),type:"file_operation",status:"completed",payload:JSON.stringify({operation:"exists",path:process.cwd(),meta:legacy}),result:JSON.stringify({legacy:!0,note:"Legacy doctor-script task accepted (no-op)."})})}}catch(err){return set.status=500,{error:"Failed to create task",details:String(err)}}let typedBody=body;try{if(typedBody.type==="command"||typedBody.type==="script"){let denied=denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK",server);if(denied)return set.status=403,{error:"Forbidden",message:denied}}else if(typedBody.type==="file_operation"&&["write","delete"].includes(String(typedBody.payload?.operation??""))){let denied=denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_FILE_TASK",server);if(denied)return set.status=403,{error:"Forbidden",message:denied}}if(concurrency.active>=MAX_CONCURRENT_TASKS)return set.status=429,{error:"Too many tasks are already running"};if(concurrency.active+=1,typedBody.callbackUrl&&!isAllowedCallbackUrl(typedBody.callbackUrl,(()=>{let gatewayConfig=gatewayClient.getConfig();return[gatewayConfig?.globalGatewayUrl,gatewayConfig?.workspaceGatewayUrl]})()))return concurrency.active=Math.max(0,concurrency.active-1),set.status=400,{error:"callbackUrl is not allowed"};let task=await db.createTask({id:globalThis.crypto.randomUUID(),type:typedBody.type,status:"pending",payload:JSON.stringify(typedBody.payload),...typedBody.calendarTaskId&&{calendarTaskId:typedBody.calendarTaskId},...typedBody.timeout&&{timeout:typedBody.timeout}});return processTask(db,task,concurrency,{timeout:typedBody.timeout,callbackUrl:typedBody.callbackUrl}),{task}}catch(err){return concurrency.active=Math.max(0,concurrency.active-1),set.status=500,{error:"Failed to create task",details:String(err)}}},{body:t.Any()}).post("/:id/cancel",async({params,set})=>{let task=await db.getTask(params.id);if(!task)return set.status=404,{error:"Task not found"};if(task.status!=="pending")return set.status=400,{error:"Only pending tasks can be cancelled"};return await db.updateTask(params.id,{status:"failed",error:"Task cancelled by user"}),{success:!0}}).delete("/:id",async({params,set})=>{let task=await db.getTask(params.id);if(!task)return set.status=404,{error:"Task not found"};if(task.status==="pending")await db.updateTask(params.id,{status:"failed",error:"Task cancelled via DELETE"});return{success:!0,status:task.status}})}var DEFAULT_AGENT_URL="http://localhost:3005";function tasksBase(merged){return`/api/profiles/${encodeURIComponent(resolveProfileName(merged))}/tasks`}function register(program){let cmd=program.command("task").description("Manage background tasks");cmd.command("list").description("List tasks").option("--status <status>","Filter by status").option("--profile <name>","Profile name (overrides default resolution)").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(options){let merged={...program.opts(),...options};try{let url=getAgentUrl(options),base=tasksBase(merged),query=options.status?`${base}/?status=${encodeURIComponent(options.status)}`:`${base}/`;await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{return(await apiGet(url,query)).tasks||[]},plain:(tasks)=>{if(!tasks||tasks.length===0){info("No tasks found.");return}header("Tasks"),formatTable(tasks.map((t2)=>({ID:shortId(t2.id),Type:t2.type||"-",Status:formatStatus(t2.status),Created:t2.createdAt?timeAgo(t2.createdAt):"-"})))},interactive:async(tasks)=>{if(!tasks||tasks.length===0){header("Tasks"),info("No tasks found.");return}let rows=tasks.map((t2)=>({id:String(t2.id),label:t2.type||shortId(t2.id),hint:t2.status||"",detail:[`ID: ${t2.id}`,`Type: ${t2.type??"-"}`,`Status: ${formatStatus(t2.status)}`,`Created: ${t2.createdAt?timeAgo(t2.createdAt):"-"}`].join(`
|
|
4
|
+
`;continue}output+=decoder.decode(chunk,{stream:!0})}return output+=decoder.decode(),output}async function executeFileOperation(payload){if(!payload.path)throw Error("Path is required");switch(payload.operation){case"read":{let safePath=(await resolveSafePath(payload.path,{mustExist:!0})).path;if(isSensitivePath(safePath))throw Error("Access to sensitive files not allowed");return await assertReadableFileSize(safePath),{content:await fs.readFile(safePath,"utf-8"),path:safePath}}case"write":{if(!payload.content)throw Error("Content is required for write operation");let safePath=(await resolveSafePath(payload.path,{forWrite:!0})).path;if(isProtectedPath(safePath)||isSensitivePath(safePath))throw Error("Write access to sensitive files not allowed");return await fs.writeFile(safePath,payload.content),{success:!0,path:safePath}}case"delete":{let safePath=(await resolveSafePath(payload.path,{mustExist:!0})).path;if(isProtectedPath(safePath)||isSensitivePath(safePath))throw Error("Delete access to this path is not allowed");return await fs.unlink(safePath),{success:!0,path:safePath}}case"exists":try{let safePath=(await resolveSafePath(payload.path)).path;return await fs.access(safePath),{exists:!0,path:safePath}}catch{return{exists:!1}}case"list":{let safePath=(await resolveSafePath(payload.path,{mustExist:!0})).path;return{files:(await listDirectoryCapped(safePath)).map((entry)=>entry.name),path:safePath}}default:throw Error(`Unknown file operation: ${payload.operation}`)}}async function processTask(db,task,concurrency,options){try{let current=await db.getTask(task.id);if(current&¤t.status!=="pending")return;await db.updateTask(task.id,{status:"running"});let payload=JSON.parse(task.payload),result,exitCode;switch(task.type){case"command":{let cmdResult=await executeCommand(payload,options?.timeout);result=cmdResult,exitCode=cmdResult.exitCode;break}case"script":{let scriptResult=await executeScript(payload,options?.timeout);result=scriptResult,exitCode=scriptResult.exitCode;break}case"file_operation":result=await executeFileOperation(payload);break;default:throw Error(`Unknown task type: ${task.type}`)}let isFailedExit=exitCode!==void 0&&exitCode!==0,finalStatus=isFailedExit?"failed":"completed";if(await db.updateTask(task.id,{status:finalStatus,result:JSON.stringify(result),...exitCode!==void 0&&{exitCode},...isFailedExit&&{error:`Process exited with code ${exitCode}`}}),options?.callbackUrl)sendCallback(options.callbackUrl,{agentTaskId:task.id,status:finalStatus,result,exitCode,...isFailedExit&&{error:`Process exited with code ${exitCode}`}})}catch(err){let errorMsg=err instanceof Error?err.message:String(err);try{await db.updateTask(task.id,{status:"failed",error:errorMsg})}catch(updateErr){console.warn(`[task] failed to mark task ${task.id} as failed: ${updateErr instanceof Error?updateErr.message:String(updateErr)}`)}if(options?.callbackUrl)sendCallback(options.callbackUrl,{agentTaskId:task.id,status:"failed",error:errorMsg})}finally{concurrency.active=Math.max(0,concurrency.active-1)}}function sendCallback(url,body){fetch(url,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body),signal:AbortSignal.timeout(1e4)}).catch(()=>{})}function createRoutes(deps){let{db}=deps,concurrency={active:0};return new Elysia().get("/",async({query})=>{if(query.status==="pending")return{tasks:await db.getPendingTasks()};return{tasks:await db.getAllTasks()}}).get("/:id",async({params,set})=>{let task=await db.getTask(params.id);if(!task)return set.status=404,{error:"Task not found"};return{task}}).post("/legacy",async({body,set})=>{try{return{task:await db.createTask({id:globalThis.crypto.randomUUID(),type:"file_operation",status:"completed",payload:JSON.stringify({operation:"exists",path:process.cwd(),meta:body}),result:JSON.stringify({legacy:!0,note:"Legacy doctor-script task accepted (no-op)."})})}}catch(err){return set.status=500,{error:"Failed to create task",details:String(err)}}},{body:t.Any()}).post("/",async({body,request,server,set})=>{let legacy=body;if(legacy&&typeof legacy==="object"&&!legacy.type&&!legacy.payload&&(legacy.title||legacy.description||legacy.command))try{return{task:await db.createTask({id:globalThis.crypto.randomUUID(),type:"file_operation",status:"completed",payload:JSON.stringify({operation:"exists",path:process.cwd(),meta:legacy}),result:JSON.stringify({legacy:!0,note:"Legacy doctor-script task accepted (no-op)."})})}}catch(err){return set.status=500,{error:"Failed to create task",details:String(err)}}let typedBody=body;try{if(typedBody.type==="command"||typedBody.type==="script"){let denied=await denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK",server,{getConfig:(key)=>db.getConfig(key)});if(denied)return set.status=403,{error:"Forbidden",message:denied}}else if(typedBody.type==="file_operation"&&["write","delete"].includes(String(typedBody.payload?.operation??""))){let denied=await denyNonLocalMutation(request,"VIBECONTROLS_ALLOW_REMOTE_FILE_TASK",server,{getConfig:(key)=>db.getConfig(key)});if(denied)return set.status=403,{error:"Forbidden",message:denied}}if(concurrency.active>=MAX_CONCURRENT_TASKS)return set.status=429,{error:"Too many tasks are already running"};if(concurrency.active+=1,typedBody.callbackUrl&&!isAllowedCallbackUrl(typedBody.callbackUrl,(()=>{let gatewayConfig=gatewayClient.getConfig();return[gatewayConfig?.globalGatewayUrl,gatewayConfig?.workspaceGatewayUrl]})()))return concurrency.active=Math.max(0,concurrency.active-1),set.status=400,{error:"callbackUrl is not allowed"};let task=await db.createTask({id:globalThis.crypto.randomUUID(),type:typedBody.type,status:"pending",payload:JSON.stringify(typedBody.payload),...typedBody.calendarTaskId&&{calendarTaskId:typedBody.calendarTaskId},...typedBody.timeout&&{timeout:typedBody.timeout}});return processTask(db,task,concurrency,{timeout:typedBody.timeout,callbackUrl:typedBody.callbackUrl}),{task}}catch(err){return concurrency.active=Math.max(0,concurrency.active-1),set.status=500,{error:"Failed to create task",details:String(err)}}},{body:t.Any()}).post("/:id/cancel",async({params,set})=>{let task=await db.getTask(params.id);if(!task)return set.status=404,{error:"Task not found"};if(task.status!=="pending")return set.status=400,{error:"Only pending tasks can be cancelled"};return await db.updateTask(params.id,{status:"failed",error:"Task cancelled by user"}),{success:!0}}).delete("/:id",async({params,set})=>{let task=await db.getTask(params.id);if(!task)return set.status=404,{error:"Task not found"};if(task.status==="pending")await db.updateTask(params.id,{status:"failed",error:"Task cancelled via DELETE"});return{success:!0,status:task.status}})}var DEFAULT_AGENT_URL="http://localhost:3005";function tasksBase(merged){return`/api/profiles/${encodeURIComponent(resolveProfileName(merged))}/tasks`}function register(program){let cmd=program.command("task").description("Manage background tasks");cmd.command("list").description("List tasks").option("--status <status>","Filter by status").option("--profile <name>","Profile name (overrides default resolution)").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(options){let merged={...program.opts(),...options};try{let url=getAgentUrl(options),base=tasksBase(merged),query=options.status?`${base}/?status=${encodeURIComponent(options.status)}`:`${base}/`;await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{return(await apiGet(url,query)).tasks||[]},plain:(tasks)=>{if(!tasks||tasks.length===0){info("No tasks found.");return}header("Tasks"),formatTable(tasks.map((t2)=>({ID:shortId(t2.id),Type:t2.type||"-",Status:formatStatus(t2.status),Created:t2.createdAt?timeAgo(t2.createdAt):"-"})))},interactive:async(tasks)=>{if(!tasks||tasks.length===0){header("Tasks"),info("No tasks found.");return}let rows=tasks.map((t2)=>({id:String(t2.id),label:t2.type||shortId(t2.id),hint:t2.status||"",detail:[`ID: ${t2.id}`,`Type: ${t2.type??"-"}`,`Status: ${formatStatus(t2.status)}`,`Created: ${t2.createdAt?timeAgo(t2.createdAt):"-"}`].join(`
|
|
5
5
|
`)}));await interactiveTable({title:`vibe task list \u2014 ${tasks.length} task(s)`,rows})},json:(tasks)=>tasks.map((t2)=>({id:t2.id,type:t2.type??null,status:t2.status??null,createdAt:t2.createdAt??null}))})}catch(err){fail(errMsg(err))}}),cmd.command("run").description("Run a new task").requiredOption("-c, --command <cmd>","Command to run").option("--cwd <dir>","Working directory").option("--profile <name>","Profile name (overrides default resolution)").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--json","Emit JSON").action(async function(options){let merged={...program.opts(),...options};try{let url=getAgentUrl(options);if(!isLocalAgentUrl(url)&&process.env.VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK!=="1"){if(merged.json){maybePrintJson(merged,{ok:!1,error:"Remote shell task refused without env override"});return}fail("Refusing to create a shell task on a remote agent URL. Set VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK=1 only if you trust the target.");return}let payload={command:options.command};if(options.cwd)payload.cwd=options.cwd;let base=tasksBase(merged),result=await apiPost(url,`${base}/`,{type:"command",payload}),id=result?.task?.id||result?.id||result?.taskId;if(maybePrintJson(merged,{ok:!0,id}))return;if(!id){fail("Task created but no ID returned by the agent");return}success(`Task created: ${shortId(id)}`)}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(errMsg(err))}}),cmd.command("cancel").description("Cancel a running task").requiredOption("-i, --id <id>","Task ID").option("--profile <name>","Profile name (overrides default resolution)").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--json","Emit JSON").action(async function(options){let merged={...program.opts(),...options};try{let url=getAgentUrl(options),base=tasksBase(merged);if(await apiPost(url,`${base}/${options.id}/cancel`,{}),maybePrintJson(merged,{ok:!0,id:options.id}))return;success(`Task ${shortId(options.id)} cancelled.`)}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(errMsg(err))}})}function registerCommands(program,_hostServices){register(program)}function createPlugin(_ctx){return{name:"task",version:"2.2.0",description:"Background task execution \u2014 commands, scripts, file operations",capabilities:{storage:"rw",subprocess:!0,audit:!0,telemetry:!0},tags:["backend","cli"],cliCommand:"task",apiPrefix:"/api/tasks",createRoutes:(deps)=>createRoutes(deps),onCliSetup:async(program,hostServices)=>{registerCommands(program,hostServices)}}}export{createPlugin};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{denyNonLocalMutation,isLikelyLocalRequest}from"./index-
|
|
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(`
|
|
2
|
+
import{denyNonLocalMutation,isLikelyLocalRequest}from"./index-7qkzdq23.js";import{autoReportToBackend,stopProfileReconciler,stopTunnelSync}from"./index-mq3amjcf.js";import{runAliasCaptured}from"./index-9e1fgxea.js";import{checkDependencies,installDependencies}from"./index-fg47j98r.js";import{ServiceManager}from"./index-wmeb40wr.js";import{redactUnknownSecrets}from"./index-y5q0m3cx.js";import{getActiveContributorRegistry}from"./index-wk359257.js";import"./index-cyhcvfxs.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
|
+
`);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=await 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=await 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=await 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(`
|
|
6
6
|
`),footer:"press q to exit"})}})}catch(err){fail(`Failed to get system info: ${errMsg(err)}`)}})}var DEFAULT_AGENT_URL4="http://localhost:3005";async function fetchHealth(agentUrl){try{let res=await fetch(`${agentUrl}/health/ready`);if(res.status===200||res.status===503)return await res.json();return{healthy:!1,status:res.status}}catch{return{healthy:!1,error:"unreachable"}}}async function fetchApiKey(agentUrl,profile){try{let res=await fetch(`${agentUrl}/api/profiles/${profile}/agent/api-key`);if(!res.ok)return null;return(await res.json()).apiKey??null}catch{return null}}async function fetchPlugins(agentUrl,profile){try{return await apiGet(agentUrl,`/api/profiles/${profile}/plugins`)}catch{return null}}function renderHealthLine(h){let hh=h??{};if(hh.error==="unreachable")return colors.red("unreachable");if(typeof hh.status==="number"&&hh.status>=400)return colors.red(`http ${hh.status}`);let status=typeof hh.status==="string"?hh.status:"",bootState=typeof hh.bootState==="string"?hh.bootState:"";if(status==="ok"&&bootState==="ready")return colors.green("ready");if(bootState==="awaiting-config")return colors.yellow("awaiting-config");if(bootState==="initializing")return colors.yellow("initializing");if(bootState==="degraded"||status==="degraded")return colors.red(`degraded${bootState?` (${bootState})`:""}`);if(hh.healthy===!1)return colors.red("unhealthy");return colors.green("healthy")}function extractLastFinalizeError(h){let lfe=(h??{}).lastFinalizeError;if(!lfe||typeof lfe!=="object")return null;let obj=lfe;if(typeof obj.message!=="string"||typeof obj.at!=="string")return null;return{message:obj.message,at:obj.at,attempt:typeof obj.attempt==="number"?obj.attempt:void 0}}function renderHealthHint(h){let hh=h??{},bootState=typeof hh.bootState==="string"?hh.bootState:"";if(bootState!=="awaiting-config"&&bootState!=="degraded")return null;let lines=[],lfe=extractLastFinalizeError(h);if(lfe)if(lines.push(`${colors.gray("\xB7")} Last error: ${colors.red(lfe.message)}`),lfe.attempt!==void 0)lines.push(`${colors.gray("\xB7")} Background retry: attempt ${lfe.attempt}`);else lines.push(`${colors.gray("\xB7")} Last seen: ${lfe.at}`);if(bootState==="awaiting-config")lines.push(`${colors.gray("\xB7")} Force retry now: ${colors.bold("vibe retry-config")}`);return lines.push(`${colors.gray("\xB7")} Full trace: ${colors.bold("vibe logs --since 10m")}`),lines.join(`
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var LOOPBACK_HOSTS=new Set(["localhost","127.0.0.1","0.0.0.0","::1","[::1]"]);function normalizedHost(hostname){return hostname.toLowerCase().replace(/^\[|\]$/g,"")}function isLikelyLocalRequest(request,server){let headers=request.headers;if(headers.has("cf-connecting-ip")||headers.has("x-forwarded-for")||headers.has("x-real-ip")||headers.has("forwarded"))return!1;let peerAddress=server?.requestIP(request)?.address;if(!peerAddress)return!1;return LOOPBACK_HOSTS.has(normalizedHost(peerAddress))}function envOverrideConfigKey(envOverride){return`security:${envOverride.toLowerCase()}`}function isTruthy(value){if(!value)return!1;let normalized=value.trim().toLowerCase();return normalized==="1"||normalized==="true"||normalized==="yes"}async function denyNonLocalMutation(request,envOverride,server,options){if(isLikelyLocalRequest(request,server))return null;if(isTruthy(process.env[envOverride]))return null;if(options?.getConfig)try{let persisted=await options.getConfig(envOverrideConfigKey(envOverride));if(isTruthy(persisted))return null}catch{}return`This operation is local-only by default. Set ${envOverride}=1 to allow it over a tunnel, or toggle it from the agent's Security Settings.`}
|
|
3
|
+
export{isLikelyLocalRequest,denyNonLocalMutation};
|