@vibecontrols/agent 2026.531.16 → 2026.531.18

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.
Files changed (56) hide show
  1. package/dist/{agent-config-y63ddxgy.js → agent-config-9vtbzkpe.js} +1 -1
  2. package/dist/{agent-ready-tracker-5yq97296.js → agent-ready-tracker-eqq4qrr1.js} +1 -1
  3. package/dist/app-w003nq2j.js +2 -0
  4. package/dist/bootstrap-workspace-g952kwey.js +2 -0
  5. package/dist/{bridge-client-nsed8aw4.js → bridge-client-r6hsx65j.js} +1 -1
  6. package/dist/cli.js +16 -2
  7. package/dist/{daemon-profile-c7zt62gk.js → daemon-profile-rgncnttb.js} +1 -1
  8. package/dist/{finalize-retry-worker-3q4xc65x.js → finalize-retry-worker-qeaxq1p5.js} +1 -1
  9. package/dist/{index-9nn2kpyg.js → index-0nzw2zq2.js} +1 -1
  10. package/dist/{index-ns2dd8nx.js → index-0z2k6j1p.js} +1 -1
  11. package/dist/{index-r0qezdp7.js → index-3y6z75rj.js} +1 -1
  12. package/dist/{index-wvabz3xh.js → index-44trf1k5.js} +1 -1
  13. package/dist/{index-4ymjceh1.js → index-83fje7jn.js} +1 -1
  14. package/dist/index-9wdrkmv7.js +4 -0
  15. package/dist/index-b0arfgtz.js +11 -0
  16. package/dist/{index-wk359257.js → index-cmk7wy4k.js} +2 -2
  17. package/dist/index-d0jamjv4.js +5 -0
  18. package/dist/{index-95nmejfd.js → index-dd04zdxh.js} +1 -1
  19. package/dist/{index-4s5hv6a7.js → index-dehc0rdg.js} +2 -2
  20. package/dist/{index-bysm7taq.js → index-ef15r9w3.js} +2 -2
  21. package/dist/{index-7pdmqbj8.js → index-empz1ez9.js} +2 -2
  22. package/dist/{index-0n663tk5.js → index-fv24g6kc.js} +1 -1
  23. package/dist/{index-wmeb40wr.js → index-grv91kbn.js} +3 -3
  24. package/dist/{index-m483e746.js → index-kycf7s0z.js} +1 -1
  25. package/dist/{index-926vt87h.js → index-n579xh6b.js} +1 -1
  26. package/dist/{index-23rdsqea.js → index-nd153dk7.js} +1 -1
  27. package/dist/{index-mq3amjcf.js → index-p5twr8tk.js} +2 -2
  28. package/dist/index-q5b845ce.js +16 -0
  29. package/dist/{index-sfjbh6fa.js → index-rp8n85d4.js} +1 -1
  30. package/dist/{index-njg2sn7v.js → index-s10qzq2q.js} +1 -1
  31. package/dist/{index-yhwmtghn.js → index-sbv5gzqv.js} +2 -2
  32. package/dist/index-xd4q7hxb.js +83 -0
  33. package/dist/{index-e875a6zy.js → index-y98cdtet.js} +1 -1
  34. package/dist/index.js +2 -2
  35. package/dist/key.cmd-eex4j8x7.js +2 -0
  36. package/dist/{path-utils-y2ba2951.js → path-utils-t8f2c5kh.js} +1 -1
  37. package/dist/plugin-system-7e2jwkgp.js +2 -0
  38. package/dist/prereqs-runner-g1cxnrbf.js +2 -0
  39. package/dist/{profile-mount-wqyjvy59.js → profile-mount-3rs7npag.js} +1 -1
  40. package/dist/secondary-profile-attach-zqjvj94y.js +2 -0
  41. package/dist/{subprocess-vks78xmn.js → subprocess-3r4rwtf1.js} +1 -1
  42. package/dist/telemetry-fyfyk4b8.js +2 -0
  43. package/dist/{tunnel-bootstrap-x82wcnt8.js → tunnel-bootstrap-fk1vkmcf.js} +1 -1
  44. package/package.json +1 -1
  45. package/dist/app-nepbpk4d.js +0 -2
  46. package/dist/bootstrap-workspace-qwed8tfg.js +0 -2
  47. package/dist/index-2pqv0bya.js +0 -11
  48. package/dist/index-b4wy3jrt.js +0 -15
  49. package/dist/index-brtw3j8x.js +0 -83
  50. package/dist/index-cyhcvfxs.js +0 -5
  51. package/dist/index-s3vbcsga.js +0 -5
  52. package/dist/key.cmd-05ckkmn5.js +0 -2
  53. package/dist/plugin-system-w3vnn5vh.js +0 -2
  54. package/dist/prereqs-runner-k85knvak.js +0 -2
  55. package/dist/secondary-profile-attach-mgt3bv4j.js +0 -2
  56. package/dist/telemetry-rew0mtj2.js +0 -2
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{BUILTIN_PROFILE_SEEDS,CURRENT_CONFIG_SCHEMA_VERSION,DEFAULT_PROFILE_NAME,NAMED_ENV_PRESETS,applyAgentConfigToEnv,applyAgentSecretsToEnv,assertValidProfileName,buildProfileSeed,deleteProfile,ensureProfilesInitialized,getActiveProfilePointerPath,getAgentConfigDefaults,getAgentConfigPath,getAgentConfigSeed,getProductionGatewayUrls,getProfilePath,getProfilesDir,listProfileNames,readActiveProfile,readActiveProfileName,readAgentConfig,readAgentConfigWithSecrets,readProfile,writeActiveProfileName,writeAgentConfig,writeAgentConfigAwaitSecrets,writeProfile}from"./index-2pqv0bya.js";import"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{writeProfile,writeAgentConfigAwaitSecrets,writeAgentConfig,writeActiveProfileName,readProfile,readAgentConfigWithSecrets,readAgentConfig,readActiveProfileName,readActiveProfile,listProfileNames,getProfilesDir,getProfilePath,getProductionGatewayUrls,getAgentConfigSeed,getAgentConfigPath,getAgentConfigDefaults,getActiveProfilePointerPath,ensureProfilesInitialized,deleteProfile,buildProfileSeed,assertValidProfileName,applyAgentSecretsToEnv,applyAgentConfigToEnv,NAMED_ENV_PRESETS,DEFAULT_PROFILE_NAME,CURRENT_CONFIG_SCHEMA_VERSION,BUILTIN_PROFILE_SEEDS};
2
+ import{BUILTIN_PROFILE_SEEDS,CURRENT_CONFIG_SCHEMA_VERSION,DEFAULT_PROFILE_NAME,NAMED_ENV_PRESETS,applyAgentConfigToEnv,applyAgentSecretsToEnv,assertValidProfileName,buildProfileSeed,deleteProfile,ensureProfilesInitialized,getActiveProfilePointerPath,getAgentConfigDefaults,getAgentConfigPath,getAgentConfigSeed,getProductionGatewayUrls,getProfilePath,getProfilesDir,listProfileNames,readActiveProfile,readActiveProfileName,readAgentConfig,readAgentConfigWithSecrets,readProfile,writeActiveProfileName,writeAgentConfig,writeAgentConfigAwaitSecrets,writeProfile}from"./index-b0arfgtz.js";import"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{writeProfile,writeAgentConfigAwaitSecrets,writeAgentConfig,writeActiveProfileName,readProfile,readAgentConfigWithSecrets,readAgentConfig,readActiveProfileName,readActiveProfile,listProfileNames,getProfilesDir,getProfilePath,getProductionGatewayUrls,getAgentConfigSeed,getAgentConfigPath,getAgentConfigDefaults,getActiveProfilePointerPath,ensureProfilesInitialized,deleteProfile,buildProfileSeed,assertValidProfileName,applyAgentSecretsToEnv,applyAgentConfigToEnv,NAMED_ENV_PRESETS,DEFAULT_PROFILE_NAME,CURRENT_CONFIG_SCHEMA_VERSION,BUILTIN_PROFILE_SEEDS};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{blank,colors,header,kv}from"./index-r0qezdp7.js";import"./index-e9rt4m94.js";var ph={pending:()=>colors.gray("\u25CF"),active:()=>colors.cyan("\u25B6"),ok:()=>colors.green("\u2713"),warn:()=>colors.yellow("\u26A0"),fail:()=>colors.red("\u2717")};function emit(line,verbose){if(verbose)console.log(" "+line)}async function fetchHealth(agentUrl,timeoutMs=2000){let ctrl=new AbortController,t=setTimeout(()=>ctrl.abort(),timeoutMs);try{let res=await fetch(`${agentUrl}/health/ready`,{signal:ctrl.signal});if(res.status!==200&&res.status!==503)return null;return await res.json()}catch{return null}finally{clearTimeout(t)}}function tunnelProvider(c){if(!c?.message)return null;let m=c.message.match(/provider:\s*(\S+)/);return m?m[1]:null}async function trackAgentReady(agentUrl,opts={}){let timeoutMs=opts.timeoutMs??60000,pollIntervalMs=opts.pollIntervalMs??500,verbose=opts.verbose!==!1;if(verbose)header("Waiting for agent to become ready");let printed={daemon:!1,awaiting:!1,initializing:!1,db:!1,session:!1,tunnel:!1},start=Date.now(),lastBootState="",awaitingObservations=0,lastSeenTunnelProvider=null;while(Date.now()-start<timeoutMs){let health=await fetchHealth(agentUrl);if(!health){await sleep(pollIntervalMs);continue}if(!printed.daemon)emit(`${ph.ok()} Daemon listening on ${agentUrl}`,verbose),printed.daemon=!0;let bootState=health.bootState??"unknown",components=health.components??{};if(bootState!==lastBootState){if(lastBootState=bootState,bootState==="awaiting-config"&&!printed.awaiting)emit(`${ph.warn()} Workspace config: ${colors.yellow("awaiting OAuth credentials \u2014 register this agent in the VibeControls UI")}`,verbose),printed.awaiting=!0;else if(bootState==="initializing"&&!printed.initializing)emit(`${ph.active()} Finalizing: fetching encryption key, opening storage, loading plugins\u2026`,verbose),printed.initializing=!0}if(bootState==="awaiting-config"){if(awaitingObservations++,awaitingObservations>=2)return{outcome:"awaiting-config",bootState,degradedReasons:[],summary:"Agent is awaiting workspace registration. Open the VibeControls UI \u2192 Agents \u2192 Add Agent, paste the API Key and Tunnel URL printed above."}}else awaitingObservations=0;if(components.db?.ok&&!printed.db)emit(`${ph.ok()} Storage encryption key loaded, DB opened`,verbose),printed.db=!0;if(components.session?.ok&&!printed.session){let msg=components.session.message??"";emit(`${ph.ok()} Plugins started \u2014 ${msg}`,verbose),printed.session=!0}let provider=tunnelProvider(components.tunnel);if(provider&&provider!=="bootstrap"&&provider!==lastSeenTunnelProvider){if(lastSeenTunnelProvider=provider,!printed.tunnel)emit(`${ph.ok()} Tunnel ready (provider: ${provider})`,verbose),printed.tunnel=!0}let overallDegraded=health.status==="degraded";if(bootState==="ready"&&!overallDegraded){if(verbose)blank(),kv("Status",colors.green("ready"));return{outcome:"ready",bootState,degradedReasons:[],summary:"Agent ready."}}if(bootState==="degraded"||bootState==="ready"&&overallDegraded){let reasons=health.degradedReasons??[],allReasons=reasons;if(reasons.length===0){let synthesized=[];for(let[name,c]of Object.entries(components)){if(name==="boot"||name==="lifecycle")continue;if(c&&!c.ok&&c.message)synthesized.push({plugin:name,message:c.message})}for(let[name,p]of Object.entries(health.plugins??{}))if(p&&!p.ok&&p.message)synthesized.push({plugin:`plugin:${name}`,message:p.message});allReasons=synthesized}if(verbose){blank(),emit(`${ph.fail()} Agent boot ${colors.red("degraded")} \u2014 investigate before retrying`,verbose);for(let r of allReasons)emit(` ${colors.gray("\xB7")} [${r.plugin}] ${r.message}`,verbose);blank()}return{outcome:"degraded",bootState,degradedReasons:allReasons,summary:allReasons.length?`Agent degraded: ${allReasons.map((r)=>`[${r.plugin}] ${r.message}`).join("; ")}`:"Agent degraded with no reported reason \u2014 check vibe logs."}}await sleep(pollIntervalMs)}if(verbose)blank(),emit(`${ph.warn()} Timed out after ${Math.round(timeoutMs/1000)}s; last bootState=${lastBootState||"unknown"}. Check ${colors.bold("vibe logs")} for details.`,verbose),blank();return{outcome:"timeout",bootState:lastBootState||"unknown",degradedReasons:[],summary:`Timed out waiting for ready. Last bootState=${lastBootState||"unknown"}.`}}function sleep(ms){return new Promise((r)=>setTimeout(r,ms))}export{trackAgentReady};
2
+ import{blank,colors,header,kv}from"./index-3y6z75rj.js";import"./index-e9rt4m94.js";var ph={pending:()=>colors.gray("\u25CF"),active:()=>colors.cyan("\u25B6"),ok:()=>colors.green("\u2713"),warn:()=>colors.yellow("\u26A0"),fail:()=>colors.red("\u2717")};function emit(line,verbose){if(verbose)console.log(" "+line)}async function fetchHealth(agentUrl,timeoutMs=2000){let ctrl=new AbortController,t=setTimeout(()=>ctrl.abort(),timeoutMs);try{let res=await fetch(`${agentUrl}/health/ready`,{signal:ctrl.signal});if(res.status!==200&&res.status!==503)return null;return await res.json()}catch{return null}finally{clearTimeout(t)}}function tunnelProvider(c){if(!c?.message)return null;let m=c.message.match(/provider:\s*(\S+)/);return m?m[1]:null}async function trackAgentReady(agentUrl,opts={}){let timeoutMs=opts.timeoutMs??60000,pollIntervalMs=opts.pollIntervalMs??500,verbose=opts.verbose!==!1;if(verbose)header("Waiting for agent to become ready");let printed={daemon:!1,awaiting:!1,initializing:!1,db:!1,session:!1,tunnel:!1},start=Date.now(),lastBootState="",awaitingObservations=0,lastSeenTunnelProvider=null;while(Date.now()-start<timeoutMs){let health=await fetchHealth(agentUrl);if(!health){await sleep(pollIntervalMs);continue}if(!printed.daemon)emit(`${ph.ok()} Daemon listening on ${agentUrl}`,verbose),printed.daemon=!0;let bootState=health.bootState??"unknown",components=health.components??{};if(bootState!==lastBootState){if(lastBootState=bootState,bootState==="awaiting-config"&&!printed.awaiting)emit(`${ph.warn()} Workspace config: ${colors.yellow("awaiting OAuth credentials \u2014 register this agent in the VibeControls UI")}`,verbose),printed.awaiting=!0;else if(bootState==="initializing"&&!printed.initializing)emit(`${ph.active()} Finalizing: fetching encryption key, opening storage, loading plugins\u2026`,verbose),printed.initializing=!0}if(bootState==="awaiting-config"){if(awaitingObservations++,awaitingObservations>=2)return{outcome:"awaiting-config",bootState,degradedReasons:[],summary:"Agent is awaiting workspace registration. Open the VibeControls UI \u2192 Agents \u2192 Add Agent, paste the API Key and Tunnel URL printed above."}}else awaitingObservations=0;if(components.db?.ok&&!printed.db)emit(`${ph.ok()} Storage encryption key loaded, DB opened`,verbose),printed.db=!0;if(components.session?.ok&&!printed.session){let msg=components.session.message??"";emit(`${ph.ok()} Plugins started \u2014 ${msg}`,verbose),printed.session=!0}let provider=tunnelProvider(components.tunnel);if(provider&&provider!=="bootstrap"&&provider!==lastSeenTunnelProvider){if(lastSeenTunnelProvider=provider,!printed.tunnel)emit(`${ph.ok()} Tunnel ready (provider: ${provider})`,verbose),printed.tunnel=!0}let overallDegraded=health.status==="degraded";if(bootState==="ready"&&!overallDegraded){if(verbose)blank(),kv("Status",colors.green("ready"));return{outcome:"ready",bootState,degradedReasons:[],summary:"Agent ready."}}if(bootState==="degraded"||bootState==="ready"&&overallDegraded){let reasons=health.degradedReasons??[],allReasons=reasons;if(reasons.length===0){let synthesized=[];for(let[name,c]of Object.entries(components)){if(name==="boot"||name==="lifecycle")continue;if(c&&!c.ok&&c.message)synthesized.push({plugin:name,message:c.message})}for(let[name,p]of Object.entries(health.plugins??{}))if(p&&!p.ok&&p.message)synthesized.push({plugin:`plugin:${name}`,message:p.message});allReasons=synthesized}if(verbose){blank(),emit(`${ph.fail()} Agent boot ${colors.red("degraded")} \u2014 investigate before retrying`,verbose);for(let r of allReasons)emit(` ${colors.gray("\xB7")} [${r.plugin}] ${r.message}`,verbose);blank()}return{outcome:"degraded",bootState,degradedReasons:allReasons,summary:allReasons.length?`Agent degraded: ${allReasons.map((r)=>`[${r.plugin}] ${r.message}`).join("; ")}`:"Agent degraded with no reported reason \u2014 check vibe logs."}}await sleep(pollIntervalMs)}if(verbose)blank(),emit(`${ph.warn()} Timed out after ${Math.round(timeoutMs/1000)}s; last bootState=${lastBootState||"unknown"}. Check ${colors.bold("vibe logs")} for details.`,verbose),blank();return{outcome:"timeout",bootState:lastBootState||"unknown",degradedReasons:[],summary:`Timed out waiting for ready. Last bootState=${lastBootState||"unknown"}.`}}function sleep(ms){return new Promise((r)=>setTimeout(r,ms))}export{trackAgentReady};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{createApp}from"./index-9wdrkmv7.js";import"./index-n579xh6b.js";import"./index-p5twr8tk.js";import"./index-skmkfyzb.js";import"./index-dd04zdxh.js";import"./index-grv91kbn.js";import"./index-y5q0m3cx.js";import"./index-cmk7wy4k.js";import"./index-d0jamjv4.js";import"./index-srbb2214.js";import"./index-rp8n85d4.js";import"./index-3jez1q8j.js";import"./index-xd4q7hxb.js";import"./index-q5b845ce.js";import"./index-d3mz9vws.js";import"./index-rnk0kny8.js";import"./index-b0arfgtz.js";import"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{createApp};
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{bootstrapWorkspace}from"./index-0z2k6j1p.js";import"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{bootstrapWorkspace};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{gatewayClient,getDaemonProfile}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";var LOG_SOURCE="bridge-client",RECONNECT_MIN_MS=2000,RECONNECT_MAX_MS=30000,PING_INTERVAL_MS=30000,FALLBACK_AFTER_FAILED_ATTEMPTS=6,state={ws:null,pingTimer:null,reconnectTimer:null,reconnectAttempt:0,connected:!1,stopped:!1,agentId:null,profile:"default",pendingEvents:[]};function log(level,message,meta){getDaemonProfile().logger[level](LOG_SOURCE,message,meta)}function resolveBridgeUrl(workspaceGatewayUrl){try{let u=new URL(workspaceGatewayUrl);u.protocol=u.protocol==="https:"?"wss:":"ws:",u.pathname="/agent-bridge",u.search="",u.hash="";let override=process.env.VIBE_BRIDGE_URL;if(override)return override;return u.toString()}catch{return process.env.VIBE_BRIDGE_URL??""}}async function connect(){if(state.stopped||state.ws)return;if(!gatewayClient.isConfigured()){log("debug","Gateway client not configured yet; deferring bridge connect");return}if(!state.agentId){log("debug","No agentId set yet; deferring bridge connect");return}let accessToken,workspaceGatewayUrl;try{let result=await gatewayClient.getAccessToken();accessToken=result.token,workspaceGatewayUrl=result.workspaceGatewayUrl}catch(err){log("warn","Failed to fetch access token for bridge",{error:err instanceof Error?err.message:String(err)}),scheduleReconnect();return}if(!accessToken||!workspaceGatewayUrl){scheduleReconnect();return}let url=resolveBridgeUrl(workspaceGatewayUrl);if(!url){log("warn","Cannot resolve bridge URL from workspaceGatewayUrl",{workspaceGatewayUrl}),scheduleReconnect();return}log("info","Connecting to agent-bridge",{url,agentId:state.agentId});let ws;try{ws=new WebSocket(url,{headers:{Authorization:`Bearer ${accessToken}`}})}catch(err){log("warn","Bridge connect threw",{error:err instanceof Error?err.message:String(err)}),scheduleReconnect();return}state.ws=ws,ws.addEventListener("open",()=>{state.reconnectAttempt=0,log("info","Bridge socket open \u2014 sending hello"),safeSend({type:"hello",agentId:state.agentId,profile:state.profile,version:process.env.npm_package_version??"unknown"})}),ws.addEventListener("message",(ev)=>{let text=typeof ev.data==="string"?ev.data:ev.data instanceof Blob?"":new TextDecoder().decode(ev.data);if(!text)return;let msg;try{msg=JSON.parse(text)}catch{return}if(msg.type==="welcome")state.connected=!0,log("info","Bridge welcome received \u2014 connection ready",{workspaceId:typeof msg.workspaceId==="string"?msg.workspaceId:void 0}),startPingLoop(),flushPending();else if(msg.type==="error")log("warn","Bridge error frame",{error:msg})}),ws.addEventListener("close",(ev)=>{if(log("info","Bridge socket closed",{code:ev.code,reason:ev.reason}),teardown(),!state.stopped)scheduleReconnect()}),ws.addEventListener("error",(ev)=>{log("warn","Bridge socket error",{message:ev.message})})}function safeSend(obj){if(!state.ws||state.ws.readyState!==1)return!1;try{return state.ws.send(JSON.stringify(obj)),!0}catch(err){return log("debug","safeSend failed",{error:err instanceof Error?err.message:String(err)}),!1}}function startPingLoop(){stopPingLoop(),state.pingTimer=setInterval(()=>{safeSend({type:"ping"})},PING_INTERVAL_MS),state.pingTimer.unref?.()}function stopPingLoop(){if(state.pingTimer)clearInterval(state.pingTimer),state.pingTimer=null}function teardown(){state.connected=!1,stopPingLoop(),state.ws=null}function scheduleReconnect(){if(state.stopped||state.reconnectTimer)return;let attempt=state.reconnectAttempt+1,delay=Math.min(RECONNECT_MIN_MS*2**Math.min(attempt-1,5),RECONNECT_MAX_MS);if(state.reconnectAttempt=attempt,attempt===FALLBACK_AFTER_FAILED_ATTEMPTS)log("warn",`Bridge unavailable after ${attempt} attempts \u2014 falling back to existing HTTP polling for heartbeat/tunnel sync. Will keep retrying in the background.`);state.reconnectTimer=setTimeout(()=>{state.reconnectTimer=null,connect()},delay),state.reconnectTimer.unref?.()}function flushPending(){if(!state.connected)return;while(state.pendingEvents.length>0){let next=state.pendingEvents.shift();if(!safeSend({type:next.type,...next.payload})){state.pendingEvents.unshift(next);return}}}var PENDING_CAP=500;function enqueue(frame){if(state.connected&&safeSend({type:frame.type,...frame.payload}))return;if(state.pendingEvents.length>=PENDING_CAP)state.pendingEvents.shift();state.pendingEvents.push(frame)}var bridgeClient={start(agentId,profile){if(state.stopped=!1,state.agentId===agentId&&state.connected)return;state.agentId=agentId,state.profile=profile||"default",connect()},stop(){if(state.stopped=!0,state.reconnectTimer)clearTimeout(state.reconnectTimer),state.reconnectTimer=null;teardown();try{state.ws?.close(1000,"agent shutdown")}catch{}},publishEvent(event){enqueue({type:"event",payload:{topic:event.topic,payload:event.payload}})},publishLog(entry){enqueue({type:"log",payload:{payload:entry}})},isConnected(){return state.connected},isFallbackEngaged(){return state.reconnectAttempt>=FALLBACK_AFTER_FAILED_ATTEMPTS&&!state.connected}};export{bridgeClient};
2
+ import{gatewayClient,getDaemonProfile}from"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";var LOG_SOURCE="bridge-client",RECONNECT_MIN_MS=2000,RECONNECT_MAX_MS=30000,PING_INTERVAL_MS=30000,FALLBACK_AFTER_FAILED_ATTEMPTS=6,state={ws:null,pingTimer:null,reconnectTimer:null,reconnectAttempt:0,connected:!1,stopped:!1,agentId:null,profile:"default",pendingEvents:[]};function log(level,message,meta){getDaemonProfile().logger[level](LOG_SOURCE,message,meta)}function resolveBridgeUrl(workspaceGatewayUrl){try{let u=new URL(workspaceGatewayUrl);u.protocol=u.protocol==="https:"?"wss:":"ws:",u.pathname="/agent-bridge",u.search="",u.hash="";let override=process.env.VIBE_BRIDGE_URL;if(override)return override;return u.toString()}catch{return process.env.VIBE_BRIDGE_URL??""}}async function connect(){if(state.stopped||state.ws)return;if(!gatewayClient.isConfigured()){log("debug","Gateway client not configured yet; deferring bridge connect");return}if(!state.agentId){log("debug","No agentId set yet; deferring bridge connect");return}let accessToken,workspaceGatewayUrl;try{let result=await gatewayClient.getAccessToken();accessToken=result.token,workspaceGatewayUrl=result.workspaceGatewayUrl}catch(err){log("warn","Failed to fetch access token for bridge",{error:err instanceof Error?err.message:String(err)}),scheduleReconnect();return}if(!accessToken||!workspaceGatewayUrl){scheduleReconnect();return}let url=resolveBridgeUrl(workspaceGatewayUrl);if(!url){log("warn","Cannot resolve bridge URL from workspaceGatewayUrl",{workspaceGatewayUrl}),scheduleReconnect();return}log("info","Connecting to agent-bridge",{url,agentId:state.agentId});let ws;try{ws=new WebSocket(url,{headers:{Authorization:`Bearer ${accessToken}`}})}catch(err){log("warn","Bridge connect threw",{error:err instanceof Error?err.message:String(err)}),scheduleReconnect();return}state.ws=ws,ws.addEventListener("open",()=>{state.reconnectAttempt=0,log("info","Bridge socket open \u2014 sending hello"),safeSend({type:"hello",agentId:state.agentId,profile:state.profile,version:process.env.npm_package_version??"unknown"})}),ws.addEventListener("message",(ev)=>{let text=typeof ev.data==="string"?ev.data:ev.data instanceof Blob?"":new TextDecoder().decode(ev.data);if(!text)return;let msg;try{msg=JSON.parse(text)}catch{return}if(msg.type==="welcome")state.connected=!0,log("info","Bridge welcome received \u2014 connection ready",{workspaceId:typeof msg.workspaceId==="string"?msg.workspaceId:void 0}),startPingLoop(),flushPending();else if(msg.type==="error")log("warn","Bridge error frame",{error:msg})}),ws.addEventListener("close",(ev)=>{if(log("info","Bridge socket closed",{code:ev.code,reason:ev.reason}),teardown(),!state.stopped)scheduleReconnect()}),ws.addEventListener("error",(ev)=>{log("warn","Bridge socket error",{message:ev.message})})}function safeSend(obj){if(!state.ws||state.ws.readyState!==1)return!1;try{return state.ws.send(JSON.stringify(obj)),!0}catch(err){return log("debug","safeSend failed",{error:err instanceof Error?err.message:String(err)}),!1}}function startPingLoop(){stopPingLoop(),state.pingTimer=setInterval(()=>{safeSend({type:"ping"})},PING_INTERVAL_MS),state.pingTimer.unref?.()}function stopPingLoop(){if(state.pingTimer)clearInterval(state.pingTimer),state.pingTimer=null}function teardown(){state.connected=!1,stopPingLoop(),state.ws=null}function scheduleReconnect(){if(state.stopped||state.reconnectTimer)return;let attempt=state.reconnectAttempt+1,delay=Math.min(RECONNECT_MIN_MS*2**Math.min(attempt-1,5),RECONNECT_MAX_MS);if(state.reconnectAttempt=attempt,attempt===FALLBACK_AFTER_FAILED_ATTEMPTS)log("warn",`Bridge unavailable after ${attempt} attempts \u2014 falling back to existing HTTP polling for heartbeat/tunnel sync. Will keep retrying in the background.`);state.reconnectTimer=setTimeout(()=>{state.reconnectTimer=null,connect()},delay),state.reconnectTimer.unref?.()}function flushPending(){if(!state.connected)return;while(state.pendingEvents.length>0){let next=state.pendingEvents.shift();if(!safeSend({type:next.type,...next.payload})){state.pendingEvents.unshift(next);return}}}var PENDING_CAP=500;function enqueue(frame){if(state.connected&&safeSend({type:frame.type,...frame.payload}))return;if(state.pendingEvents.length>=PENDING_CAP)state.pendingEvents.shift();state.pendingEvents.push(frame)}var bridgeClient={start(agentId,profile){if(state.stopped=!1,state.agentId===agentId&&state.connected)return;state.agentId=agentId,state.profile=profile||"default",connect()},stop(){if(state.stopped=!0,state.reconnectTimer)clearTimeout(state.reconnectTimer),state.reconnectTimer=null;let ws=state.ws;try{ws?.close(1000,"agent shutdown")}catch{}teardown()},publishEvent(event){enqueue({type:"event",payload:{topic:event.topic,payload:event.payload}})},publishLog(entry){enqueue({type:"log",payload:{payload:entry}})},isConnected(){return state.connected},isFallbackEngaged(){return state.reconnectAttempt>=FALLBACK_AFTER_FAILED_ATTEMPTS&&!state.connected}};export{bridgeClient};
package/dist/cli.js CHANGED
@@ -1,6 +1,20 @@
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-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(`
3
+ import{pruneStaleBunBinShims}from"./index-6xb3dqrh.js";import{listInstalledPrereqs}from"./index-nd153dk7.js";import{runAliasInherit}from"./index-9e1fgxea.js";import{checkDependencies,installDependencies}from"./index-fg47j98r.js";import{clearTunnelState,getBootstrapTunnelUrl,isProcessAlive,listPersistedTunnels,readTunnelState}from"./index-dd04zdxh.js";import{ServiceManager}from"./index-grv91kbn.js";import{REDACTED_VALUE,redactUnknownSecrets}from"./index-y5q0m3cx.js";import{CliContributorRegistry,createIframeBridge,getActiveContributorRegistry,setActiveContributorRegistry}from"./index-cmk7wy4k.js";import{ABILITIES,PLUGIN_CATALOG,PluginManager,ServiceRegistry,collectPluginBinaries,getDefaultPluginsForAbilities,resolvePluginByShortName}from"./index-d0jamjv4.js";import{getOsAdapter}from"./index-srbb2214.js";import{telemetryService}from"./index-rp8n85d4.js";import{validateEvent}from"./index-3jez1q8j.js";import{loadAgentApiKey}from"./index-q5b845ce.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-44trf1k5.js";import{blank,colors,errMsg,fail,formatStatus,formatTable,header,icons,info,kv,printAgentDetails,setColorsEnabled,shortId,success,timeAgo,warn}from"./index-3y6z75rj.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-b0arfgtz.js";import{assertAllowedGatewayUrl,gatewayClient,getDaemonProfile}from"./index-empz1ez9.js";import{getPluginRegistry,getProfileDataDir,getVibecontrolsDir,getVibecontrolsProductDir,getVibecontrolsProfile,getVibecontrolsProfilesDir}from"./index-ef15r9w3.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(err){process.stdout.write(`
4
+ [vibe] Falling back to plain prompt \u2014 ${err instanceof Error?err.message:String(err)}
5
+
6
+ `);let fallback=await plainCheckboxPrompt("Select the agent abilities to install",ABILITIES.map((a)=>({key:a.key,label:a.label,description:a.description,mandatory:a.mandatory})),defaults);if(fallback){for(let a of ABILITIES)if(a.mandatory)fallback.add(a.key);return fallback}return defaults}}async function plainCheckboxPrompt(title,choices,initiallySelected){let selected=new Set(initiallySelected);for(let c of choices)if(c.mandatory)selected.add(c.key);let print=()=>{process.stdout.write(`
7
+ ${title}
8
+ `),choices.forEach((c,i)=>{let box=c.mandatory||selected.has(c.key)?"[x]":"[ ]",lock=c.mandatory?" (required)":"";process.stdout.write(` ${(i+1).toString().padStart(2)}. ${box} ${c.label}${lock}
9
+ ${c.description}
10
+ `)}),process.stdout.write(`
11
+ Type numbers to toggle (e.g. '2 4'), 'a' = all, 'n' = none, '?' = help, Enter = confirm: `)},readLine=()=>new Promise((resolve)=>{let chunks=[],onData=(chunk)=>{let text=chunk.toString("utf8");if(chunks.push(text),text.includes(`
12
+ `))process.stdin.off("data",onData),resolve(chunks.join("").replace(/\r?\n$/u,""))};process.stdin.on("data",onData)});process.stdin.resume();try{while(!0){print();let line=(await readLine()).trim();if(line==="")return selected;if(line==="?"||line.toLowerCase()==="h"){process.stdout.write(`
13
+ \u2022 Type one or more row numbers (separated by spaces or commas) to flip those rows.
14
+ `+` \u2022 Type 'a' to select all, 'n' to deselect everything except required rows.
15
+ `+` \u2022 Press Enter on a blank line to confirm the current selection.
16
+ `+` \u2022 Press Ctrl-C to cancel and accept the safe defaults (everything on).
17
+ `);continue}if(line.toLowerCase()==="a"){for(let c of choices)selected.add(c.key);continue}if(line.toLowerCase()==="n"){selected.clear();for(let c of choices)if(c.mandatory)selected.add(c.key);continue}let tokens=line.split(/[\s,]+/u).filter(Boolean);for(let tok of tokens){let n=Number.parseInt(tok,10);if(!Number.isFinite(n)||n<1||n>choices.length)continue;let c=choices[n-1];if(!c||c.mandatory)continue;if(selected.has(c.key))selected.delete(c.key);else selected.add(c.key)}}}finally{process.stdin.pause()}}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-9vtbzkpe.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-g952kwey.js");bootstrapWorkspace();let{createApp}=await import("./app-w003nq2j.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-eqq4qrr1.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-eqq4qrr1.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
18
  `)}});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
19
  COMPREPLY=($(compgen -W "${g.subs.join(" ")}" -- "\${cur}"))
6
20
  return 0
@@ -208,7 +222,7 @@ Version: ${dump.agent.version??"n/a"}`}),probes.push({name:"System snapshot",o
208
222
  `).filter(Boolean).length>0,message:`${dump.processes.split(`
209
223
  `).filter(Boolean).length} process(es)`,detail:dump.processes||"(none found)"}),probes}function register13(program){program.command("diagnostics").alias("diag").description("Collect a redacted diagnostic dump for support").option("--agent-url <url>","Agent URL (default http://localhost:3005)","http://localhost:3005").option("--lines <n>","Tail this many log lines","1000").option("--out <path>","Write JSON dump to file").option("--json","Emit JSON to stdout").option("--plain","Force plain text output").action(async function(options){try{let merged={...program.opts(),...options},url=getAgentUrl(merged),profile=encodeURIComponent(resolveProfileName(merged)),linesN=Number.parseInt(merged.lines??"1000",10)||1000;await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{let[system,agentVersion,health]=await Promise.all([fetchSystem(url,profile),fetchAgentVersion(url,profile),fetchHealth(url)]),cfg=readAgentConfig(),cfgRedacted=redactUnknownSecrets(cfg),logsDir=join9(getVibecontrolsDir(),"logs"),logFile=["default.log","agent.log"].map((n)=>join9(logsDir,n)).find((p)=>existsSync8(p))??join9(logsDir,"default.log"),tail=readLastLines(logFile,linesN),dump={timestamp:new Date().toISOString(),agent:{version:agentVersion,agentUrl:url},system,health,config:cfgRedacted,processes:listProcesses(),logFile,logsTail:tail},probes=buildProbes(dump);return{dump,probes}},plain:(data)=>{header("VibeControls Agent Diagnostics"),blank(),info("Collecting agent state\u2026");let outPath=merged.out?merged.out:join9(mkdtempSync(join9(tmpdir(),"vibe-diag-")),`vibe-diagnostics-${Date.now()}.json`);writeFileSync6(outPath,JSON.stringify(data.dump,null,2),"utf8"),kv("Wrote",colors.bold(outPath)),kv("Log lines",String(data.dump.logsTail.length)),kv("Processes",String(data.dump.processes.split(`
210
224
  `).filter(Boolean).length)),blank(),success("Diagnostics complete. Attach the JSON to your support ticket.")},interactive:async(data)=>{let rows=data.probes.map((p,i)=>({id:`${i}-${p.name}`,label:p.name,hint:p.ok?"ok":"fail",detail:[`${p.ok?colors.green("\u2714 ok"):colors.red("\u2718 fail")} ${colors.bold(p.name)}`,"",p.message,...p.detail?["",p.detail]:[]].join(`
211
- `)})),okCount=data.probes.filter((p)=>p.ok).length;await interactiveTable({title:`Vibe Diagnostics \u2014 ${okCount}/${data.probes.length} ok`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"})},json:(data)=>({checks:data.probes.map((p)=>({name:p.name,ok:p.ok,message:p.message})),dump:data.dump})})}catch(err){fail(`Diagnostics failed: ${err instanceof Error?errMsg(err):String(err)}`)}})}import{existsSync as existsSync10,readFileSync as readFileSync7,readdirSync as readdirSync2,renameSync,rmSync as rmSync2,statSync as statSync2,writeFileSync as writeFileSync7}from"fs";import{join as join11}from"path";import{existsSync as existsSync9,readdirSync,rmSync,statSync}from"fs";import{dirname,join as join10}from"path";var SCOPE="@vibecontrols";function resolveNpmGlobalNodeModulesDir(){let binDir=getOsAdapter().npmGlobalBin();if(!binDir)return null;let posixCandidate=join10(dirname(binDir),"lib","node_modules");if(existsSync9(posixCandidate))return posixCandidate;let winCandidate=join10(dirname(binDir),"node_modules");if(existsSync9(winCandidate))return winCandidate;let dotBinSibling=join10(dirname(binDir),"..","node_modules");if(existsSync9(dotBinSibling))return dotBinSibling;return null}function resolveBunGlobalNodeModulesDir(){let adapter=getOsAdapter(),binDir=adapter.bunGlobalBin();if(binDir){let candidate=join10(dirname(binDir),"node_modules");if(existsSync9(candidate))return candidate;let fallback=join10(adapter.homeDir,".bun","install","global","node_modules");if(existsSync9(fallback))return fallback}return null}function listScopeMembers(nodeModulesDir){let scopeDir=join10(nodeModulesDir,SCOPE);if(!existsSync9(scopeDir))return[];try{return readdirSync(scopeDir).filter((name)=>{if(name.startsWith("."))return!1;try{return statSync(join10(scopeDir,name)).isDirectory()}catch{return!1}})}catch{return[]}}function listGlobalVibecontrolsPlugins(){let out=[],bunRoot=resolveBunGlobalNodeModulesDir();if(bunRoot)for(let name of listScopeMembers(bunRoot))out.push({packageName:`${SCOPE}/${name}`,root:"bun",rootDir:join10(bunRoot,SCOPE)});let npmRoot=resolveNpmGlobalNodeModulesDir();if(npmRoot)for(let name of listScopeMembers(npmRoot))out.push({packageName:`${SCOPE}/${name}`,root:"npm",rootDir:join10(npmRoot,SCOPE)});return out}function uninstallGlobalPlugin(pkg,opts){if(opts.dryRun)return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"skip"};let adapter=getOsAdapter();if(pkg.root==="bun"){let bunBin=adapter.platform==="win32"?"bun.exe":"bun";try{if(Bun.spawnSync([bunBin,"remove","-g",pkg.packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"}).exitCode===0)return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"bun"}}catch{}}else{let npmBin=adapter.platform==="win32"?"npm.cmd":"npm";try{if(Bun.spawnSync([npmBin,"uninstall","-g",pkg.packageName],{timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode===0)return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"npm"}}catch{}}let dir=join10(pkg.rootDir,pkg.packageName.slice(SCOPE.length+1));try{if(existsSync9(dir))rmSync(dir,{recursive:!0,force:!0});return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"rm"}}catch(err){return{packageName:pkg.packageName,root:pkg.root,ok:!1,via:"rm",error:err instanceof Error?err.message:String(err)}}}var AGENT_PACKAGE_NAME=`${SCOPE}/agent`;function isAgentSelf(packageName){return packageName===AGENT_PACKAGE_NAME}var VALID_KEEP_FLAGS=new Set(["db","plugins-registry","logs","binaries","subprocs"]);function parseKeepFlags(input){let keep=new Set,unknown=[];if(!input)return{keep,unknown};for(let raw of input.split(",").map((s)=>s.trim()).filter(Boolean))if(VALID_KEEP_FLAGS.has(raw))keep.add(raw);else unknown.push(raw);return{keep,unknown}}var SAFE_PROFILE_RE=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function isSafeAgentDir(parent,child){if(!SAFE_PROFILE_RE.test(child))return!1;let full=join11(parent,child);try{return statSync2(full).isDirectory()}catch{return!1}}function listAgentTargets(){let profilesRoot=getVibecontrolsProfilesDir();if(!existsSync10(profilesRoot))return[];return readdirSync2(profilesRoot).filter((id)=>isSafeAgentDir(profilesRoot,id)).map((profile)=>({profile,agentDir:join11(profilesRoot,profile)}))}function currentAgentTarget(){let profile=process.env.VIBECONTROLS_PROFILE||"default";return{profile,agentDir:join11(getVibecontrolsProfilesDir(),profile)}}function parsePluginList(input){if(!input)return[];return input.split(",").map((s)=>s.trim()).filter(Boolean)}function resolveRequestedPlugins(raw){let resolved=[],unknown=[],bundled=[];for(let name of raw){let entry=resolvePluginByShortName(name);if(!entry){unknown.push(name);continue}let r={packageName:entry.packageName,pluginName:entry.pluginName,isCore:!!entry.isCore,raw:name};if(r.isCore)bundled.push(r);else resolved.push(r)}return{resolved,unknown,bundled}}async function killTrackedSubprocs(){return(await import("./subprocess-vks78xmn.js")).killAllTracked()}function killPersistedTunnelForAgent(agentDir,dryRun){let state=readTunnelState(agentDir);if(!state||typeof state.pid!=="number"||state.pid<=0)return{killed:0,skipped:0};let os=getOsAdapter();if(!os.isProcessAlive(state.pid)){if(!dryRun)clearTunnelState(agentDir);return{killed:0,skipped:1}}if(dryRun)return info(` would kill tunnel pid ${state.pid} (${state.provider}) from ${colors.dim(agentDir)}`),{killed:1,skipped:0};let ok=os.killProcessTree(state.pid,"SIGTERM");if(ok&&os.isProcessAlive(state.pid))os.killProcessTree(state.pid,"SIGKILL");return clearTunnelState(agentDir),{killed:ok?1:0,skipped:ok?0:1}}function getBunGlobalDir(){let home=process.env.HOME??process.env.USERPROFILE??"";return join11(home,".bun","install","global")}function cleanBunGlobalManifest(pattern,dryRun){let dir=getBunGlobalDir(),manifestPath=join11(dir,"package.json"),lockPath=join11(dir,"bun.lock");if(!existsSync10(manifestPath))return{droppedCount:0,manifestPath,lockPath};let matcher=typeof pattern==="function"?pattern:(k)=>k===pattern,raw;try{raw=readFileSync7(manifestPath,"utf8")}catch{return{droppedCount:0,manifestPath,lockPath}}let pkg;try{pkg=JSON.parse(raw)}catch{return{droppedCount:0,manifestPath,lockPath}}if(!pkg.dependencies||typeof pkg.dependencies!=="object")return{droppedCount:0,manifestPath,lockPath};let before=Object.keys(pkg.dependencies),after={};for(let k of before)if(!matcher(k))after[k]=pkg.dependencies[k];let droppedCount=before.length-Object.keys(after).length;if(droppedCount===0)return{droppedCount:0,manifestPath,lockPath};if(dryRun)return info(` would drop ${droppedCount} dep(s) from ${colors.dim(manifestPath)} and remove ${colors.dim(lockPath)}`),{droppedCount,manifestPath,lockPath};pkg.dependencies=after;let tmp=`${manifestPath}.${process.pid}.tmp`;if(writeFileSync7(tmp,`${JSON.stringify(pkg,null,2)}
225
+ `)})),okCount=data.probes.filter((p)=>p.ok).length;await interactiveTable({title:`Vibe Diagnostics \u2014 ${okCount}/${data.probes.length} ok`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"})},json:(data)=>({checks:data.probes.map((p)=>({name:p.name,ok:p.ok,message:p.message})),dump:data.dump})})}catch(err){fail(`Diagnostics failed: ${err instanceof Error?errMsg(err):String(err)}`)}})}import{existsSync as existsSync10,readFileSync as readFileSync7,readdirSync as readdirSync2,renameSync,rmSync as rmSync2,statSync as statSync2,writeFileSync as writeFileSync7}from"fs";import{join as join11}from"path";import{existsSync as existsSync9,readdirSync,rmSync,statSync}from"fs";import{dirname,join as join10}from"path";var SCOPE="@vibecontrols";function resolveNpmGlobalNodeModulesDir(){let binDir=getOsAdapter().npmGlobalBin();if(!binDir)return null;let posixCandidate=join10(dirname(binDir),"lib","node_modules");if(existsSync9(posixCandidate))return posixCandidate;let winCandidate=join10(dirname(binDir),"node_modules");if(existsSync9(winCandidate))return winCandidate;let dotBinSibling=join10(dirname(binDir),"..","node_modules");if(existsSync9(dotBinSibling))return dotBinSibling;return null}function resolveBunGlobalNodeModulesDir(){let adapter=getOsAdapter(),binDir=adapter.bunGlobalBin();if(binDir){let candidate=join10(dirname(binDir),"node_modules");if(existsSync9(candidate))return candidate;let fallback=join10(adapter.homeDir,".bun","install","global","node_modules");if(existsSync9(fallback))return fallback}return null}function listScopeMembers(nodeModulesDir){let scopeDir=join10(nodeModulesDir,SCOPE);if(!existsSync9(scopeDir))return[];try{return readdirSync(scopeDir).filter((name)=>{if(name.startsWith("."))return!1;try{return statSync(join10(scopeDir,name)).isDirectory()}catch{return!1}})}catch{return[]}}function listGlobalVibecontrolsPlugins(){let out=[],bunRoot=resolveBunGlobalNodeModulesDir();if(bunRoot)for(let name of listScopeMembers(bunRoot))out.push({packageName:`${SCOPE}/${name}`,root:"bun",rootDir:join10(bunRoot,SCOPE)});let npmRoot=resolveNpmGlobalNodeModulesDir();if(npmRoot)for(let name of listScopeMembers(npmRoot))out.push({packageName:`${SCOPE}/${name}`,root:"npm",rootDir:join10(npmRoot,SCOPE)});return out}function uninstallGlobalPlugin(pkg,opts){if(opts.dryRun)return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"skip"};let adapter=getOsAdapter();if(pkg.root==="bun"){let bunBin=adapter.platform==="win32"?"bun.exe":"bun";try{if(Bun.spawnSync([bunBin,"remove","-g",pkg.packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"}).exitCode===0)return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"bun"}}catch{}}else{let npmBin=adapter.platform==="win32"?"npm.cmd":"npm";try{if(Bun.spawnSync([npmBin,"uninstall","-g",pkg.packageName],{timeout:120000,stdout:"pipe",stderr:"pipe"}).exitCode===0)return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"npm"}}catch{}}let dir=join10(pkg.rootDir,pkg.packageName.slice(SCOPE.length+1));try{if(existsSync9(dir))rmSync(dir,{recursive:!0,force:!0});return{packageName:pkg.packageName,root:pkg.root,ok:!0,via:"rm"}}catch(err){return{packageName:pkg.packageName,root:pkg.root,ok:!1,via:"rm",error:err instanceof Error?err.message:String(err)}}}var AGENT_PACKAGE_NAME=`${SCOPE}/agent`;function isAgentSelf(packageName){return packageName===AGENT_PACKAGE_NAME}var VALID_KEEP_FLAGS=new Set(["db","plugins-registry","logs","binaries","subprocs"]);function parseKeepFlags(input){let keep=new Set,unknown=[];if(!input)return{keep,unknown};for(let raw of input.split(",").map((s)=>s.trim()).filter(Boolean))if(VALID_KEEP_FLAGS.has(raw))keep.add(raw);else unknown.push(raw);return{keep,unknown}}var SAFE_PROFILE_RE=/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;function isSafeAgentDir(parent,child){if(!SAFE_PROFILE_RE.test(child))return!1;let full=join11(parent,child);try{return statSync2(full).isDirectory()}catch{return!1}}function listAgentTargets(){let profilesRoot=getVibecontrolsProfilesDir();if(!existsSync10(profilesRoot))return[];return readdirSync2(profilesRoot).filter((id)=>isSafeAgentDir(profilesRoot,id)).map((profile)=>({profile,agentDir:join11(profilesRoot,profile)}))}function currentAgentTarget(){let profile=process.env.VIBECONTROLS_PROFILE||"default";return{profile,agentDir:join11(getVibecontrolsProfilesDir(),profile)}}function parsePluginList(input){if(!input)return[];return input.split(",").map((s)=>s.trim()).filter(Boolean)}function resolveRequestedPlugins(raw){let resolved=[],unknown=[],bundled=[];for(let name of raw){let entry=resolvePluginByShortName(name);if(!entry){unknown.push(name);continue}let r={packageName:entry.packageName,pluginName:entry.pluginName,isCore:!!entry.isCore,raw:name};if(r.isCore)bundled.push(r);else resolved.push(r)}return{resolved,unknown,bundled}}async function killTrackedSubprocs(){return(await import("./subprocess-3r4rwtf1.js")).killAllTracked()}function killPersistedTunnelForAgent(agentDir,dryRun){let state=readTunnelState(agentDir);if(!state||typeof state.pid!=="number"||state.pid<=0)return{killed:0,skipped:0};let os=getOsAdapter();if(!os.isProcessAlive(state.pid)){if(!dryRun)clearTunnelState(agentDir);return{killed:0,skipped:1}}if(dryRun)return info(` would kill tunnel pid ${state.pid} (${state.provider}) from ${colors.dim(agentDir)}`),{killed:1,skipped:0};let ok=os.killProcessTree(state.pid,"SIGTERM");if(ok&&os.isProcessAlive(state.pid))os.killProcessTree(state.pid,"SIGKILL");return clearTunnelState(agentDir),{killed:ok?1:0,skipped:ok?0:1}}function getBunGlobalDir(){let home=process.env.HOME??process.env.USERPROFILE??"";return join11(home,".bun","install","global")}function cleanBunGlobalManifest(pattern,dryRun){let dir=getBunGlobalDir(),manifestPath=join11(dir,"package.json"),lockPath=join11(dir,"bun.lock");if(!existsSync10(manifestPath))return{droppedCount:0,manifestPath,lockPath};let matcher=typeof pattern==="function"?pattern:(k)=>k===pattern,raw;try{raw=readFileSync7(manifestPath,"utf8")}catch{return{droppedCount:0,manifestPath,lockPath}}let pkg;try{pkg=JSON.parse(raw)}catch{return{droppedCount:0,manifestPath,lockPath}}if(!pkg.dependencies||typeof pkg.dependencies!=="object")return{droppedCount:0,manifestPath,lockPath};let before=Object.keys(pkg.dependencies),after={};for(let k of before)if(!matcher(k))after[k]=pkg.dependencies[k];let droppedCount=before.length-Object.keys(after).length;if(droppedCount===0)return{droppedCount:0,manifestPath,lockPath};if(dryRun)return info(` would drop ${droppedCount} dep(s) from ${colors.dim(manifestPath)} and remove ${colors.dim(lockPath)}`),{droppedCount,manifestPath,lockPath};pkg.dependencies=after;let tmp=`${manifestPath}.${process.pid}.tmp`;if(writeFileSync7(tmp,`${JSON.stringify(pkg,null,2)}
212
226
  `,{encoding:"utf8"}),renameSync(tmp,manifestPath),existsSync10(lockPath))try{rmSync2(lockPath,{force:!0})}catch{}return{droppedCount,manifestPath,lockPath}}async function rmPathSafely(path,dryRun){if(!existsSync10(path))return;if(dryRun){info(` would remove ${colors.dim(path)}`);return}try{rmSync2(path,{recursive:!0,force:!0})}catch(err){warn(`Failed to remove ${path}: ${err instanceof Error?errMsg(err):String(err)}`)}}async function uninstallExternalPlugin(packageName,dryRun,agentUrl){if(dryRun)return info(` would uninstall plugin ${colors.bold(packageName)}`),!0;if(agentUrl)try{if((await fetch(`${agentUrl}/api/plugins/remove`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({packageName})})).ok)return!0}catch{}try{let bunBin=process.platform==="win32"?"bun.exe":"bun";if(Bun.spawnSync([bunBin,"remove","-g",packageName],{timeout:60000,stdout:"pipe",stderr:"pipe"}).exitCode===0)return!0}catch{}let bunGlobalPkgDir=join11(getBunGlobalDir(),"node_modules",packageName),removedFromDisk=!1;try{if(existsSync10(bunGlobalPkgDir))rmSync2(bunGlobalPkgDir,{recursive:!0,force:!0}),removedFromDisk=!0}catch{}cleanBunGlobalManifest(packageName,!1);try{let{pruneStaleBunBinShims:pruneStaleBunBinShims2}=await import("./prune-stale-shims-kfth5bta.js");pruneStaleBunBinShims2()}catch{}return removedFromDisk||!0}async function callPluginPrereqsUninstall(agentUrl,packageName){try{let res=await fetch(`${agentUrl}/api/plugins/prereqs/uninstall`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({packageName})});if(!res.ok)return{ok:!1,pendingSudo:[]};let data=await res.json();return{ok:!!data.ok,pendingSudo:data.pendingSudo??[]}}catch{return{ok:!1,pendingSudo:[]}}}async function callPluginNuke(agentUrl,packageName,dryRun){try{let res=await fetch(`${agentUrl}/api/plugins/nuke`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(packageName?{packageName,dryRun}:{dryRun})});if(!res.ok)return[];let data=await res.json(),out=[];for(let r of data.results??[]){if(r.error){out.push(`${r.plugin} (failed: ${r.error})`);continue}for(let item of r.reaped??[])out.push(`${r.plugin}: ${item}`)}return out}catch{return[]}}async function nukeAgent(target,opts){header(`Nuking agent: ${target.profile}`),kv("Path",target.agentDir);let agentUrl=null;try{let inst=(await opts.serviceManager.listInstances()).find((i)=>i.name===target.profile);if(inst?.status==="running"){if(agentUrl=`http://localhost:${inst.config?.port??3005}`,(!opts.keep.has("binaries")||!opts.keep.has("subprocs"))&&(opts.nukeAllPlugins||opts.removePlugins.length>0)){let pkgs=opts.nukeAllPlugins?[void 0]:opts.removePlugins.map((p)=>p.packageName);for(let pkg of pkgs){let reaped=await callPluginNuke(agentUrl,pkg,opts.dryRun);for(let r of reaped)if(opts.dryRun)info(` would nuke \u2192 ${r}`);else success(`Plugin nuke \u2192 ${r}`)}}if(opts.dryRun)info(` would stop running agent ${colors.bold(target.profile)}`);else try{await opts.serviceManager.stop(target.profile),success(`Stopped ${colors.bold(target.profile)}`)}catch(err){warn(`Failed to stop ${target.profile}: ${err}`)}}}catch{}if(!opts.dryRun&&!opts.keep.has("subprocs"))try{let{reaped}=await killTrackedSubprocs();if(reaped>0)success(`Reaped ${reaped} tracked subprocess(es)`)}catch{}else if(opts.keep.has("subprocs"))info(" keeping tracked subprocesses (--keep includes subprocs)");if(!opts.keep.has("subprocs"))try{let{killed}=killPersistedTunnelForAgent(target.agentDir,opts.dryRun);if(killed>0&&!opts.dryRun)success(`Killed persisted tunnel subprocess for ${target.profile}`)}catch(err){warn(`Failed to kill tunnel for ${target.profile}: ${err instanceof Error?errMsg(err):String(err)}`)}let pendingSudo=[];if(!opts.keep.has("binaries")){let tracked=listInstalledPrereqs(target.agentDir),byPlugin=new Map;for(let t of tracked){let arr=byPlugin.get(t.pluginPackageName)??[];arr.push(t),byPlugin.set(t.pluginPackageName,arr)}if(byPlugin.size===0)info(" no agent-installed prerequisites tracked");else if(!agentUrl)warn(` agent not running \u2014 cannot call prereqs/uninstall on ${byPlugin.size} plugin(s); rerun with the agent up to clean binaries`);else for(let[pkg,items]of byPlugin){if(opts.dryRun){info(` would call prereqs/uninstall on ${colors.bold(pkg)} (${items.length} item(s))`);continue}let r=await callPluginPrereqsUninstall(agentUrl,pkg);if(r.ok)success(`Uninstalled prereqs for ${pkg}`);else warn(`Plugin ${pkg} did not implement prereqs/uninstall`);for(let ps of r.pendingSudo)pendingSudo.push(ps)}}else info(" keeping installed binaries (--keep includes binaries)");for(let p of opts.removePlugins)if(await uninstallExternalPlugin(p.packageName,opts.dryRun,agentUrl))success(`Removed plugin ${p.packageName}`);else warn(`Failed to remove plugin ${p.packageName}`);if(opts.nukeAllPlugins||!opts.keep.has("plugins-registry"))await rmPathSafely(join11(target.agentDir,"agent-plugins"),opts.dryRun);else info(" keeping local agent-plugins store (--keep includes plugins-registry)");if(!opts.keep.has("db"))await rmPathSafely(join11(target.agentDir,"agent-db"),opts.dryRun);else info(" keeping encrypted DB (--keep includes db)");if(!opts.keep.has("plugins-registry"))await rmPathSafely(join11(target.agentDir,"plugins.json"),opts.dryRun),await rmPathSafely(join11(target.agentDir,"prereqs-installed.json"),opts.dryRun);else info(" keeping plugins.json + prereqs-installed.json (--keep includes plugins-registry)");if(!opts.keep.has("logs"))await rmPathSafely(join11(target.agentDir,"logs"),opts.dryRun);else info(" keeping log files (--keep includes logs)");if(opts.removeConfig)await rmPathSafely(join11(target.agentDir,"config.json"),opts.dryRun),success(`Removed config.json for ${target.profile}`);return{pendingSudo}}function register14(program){program.command("nuke").description("Cleanly remove agent state, tracked binaries, and (optionally) plugins/config across one or all agents.").option("--remove-plugins <csv>","Comma-separated plugin names (short or full) to uninstall").option("--remove-config","Also delete config.json").option("--all","Apply to every agent under .boff/vibecontrols/agents/ and wipe all external provider plugins").option("--keep <csv>","Comma-separated list of things to preserve. Valid: db, plugins-registry, logs, binaries, subprocs").option("--dry-run","Print what would happen without doing it").option("-y, --yes","Skip the confirmation prompt").option("--json","Emit JSON").action(async function(opts){let merged={...program.opts(),...opts};try{let targets=opts.all?listAgentTargets():[currentAgentTarget()].filter((t)=>existsSync10(t.agentDir));if(targets.length===0){info("Nothing to nuke \u2014 no agent state directories found.");return}let requestedRaw=parsePluginList(opts.removePlugins),{resolved,unknown,bundled}=resolveRequestedPlugins(requestedRaw);if(unknown.length>0){fail(`Unknown plugin(s): ${unknown.join(", ")} \u2014 try the full @vibecontrols/* package name`);return}if(bundled.length>0)for(let b of bundled)warn(`Skipping ${colors.bold(b.packageName)}: bundled with the agent (uninstall the agent itself with \`npm uninstall -g @vibecontrols/agent\`)`);let{keep,unknown:badKeep}=parseKeepFlags(opts.keep);if(badKeep.length>0){fail(`Unknown --keep value(s): ${badKeep.join(", ")} \u2014 valid: db, plugins-registry, logs, binaries, subprocs`);return}if(header(opts.dryRun?"Dry-run nuke":"Nuke"),info(`Agents: ${targets.map((t)=>t.profile).join(", ")}`),info(`Plugins: ${resolved.map((p)=>p.packageName).join(", ")||"(none)"}`),info(`Remove config: ${opts.removeConfig?"yes":"no"}`),info(`Keep: ${keep.size>0?[...keep].sort().join(", "):"(nothing)"}`),info(`Wipe bun-global @vibecontrols/*: ${opts.all?"yes":"no"}`),blank(),!opts.yes&&!opts.dryRun){if(!await promptConfirm(colors.bold("This will permanently delete the listed state. Continue?"))){info("Cancelled.");return}blank()}let serviceManager=new ServiceManager,allPendingSudo=[];for(let target of targets){let{pendingSudo}=await nukeAgent(target,{serviceManager,removePlugins:resolved,nukeAllPlugins:!!opts.all,removeConfig:!!opts.removeConfig,keep,dryRun:!!opts.dryRun});for(let ps of pendingSudo)allPendingSudo.push(ps);blank()}let allRemovals=[],secretsResult=null;if(opts.all){try{if(opts.dryRun){let preview=await previewClearAllSecrets();secretsResult=preview,info(` would clear ${preview.count} secret(s) from ${preview.backend}`)}else{let preview=await previewClearAllSecrets(),proceed=!0;if(preview.count>0&&!opts.yes)proceed=await promptConfirm(colors.bold(`About to remove ${preview.count} secret(s) from ${preview.backend}. Continue?`));if(proceed)if(secretsResult=await clearAllSecrets(),secretsResult.count>0)success(`Cleared ${secretsResult.count} secret(s) from ${secretsResult.backend}`);else info(" no secrets to clear");else info(" skipped clearing secrets (user declined)"),secretsResult={count:0,backend:"none",names:[]}}}catch(err){warn(`Failed to clear secrets: ${err instanceof Error?errMsg(err):String(err)}`)}let discovered=listGlobalVibecontrolsPlugins().filter((p)=>!isAgentSelf(p.packageName));if(discovered.length===0)info(" no @vibecontrols/* packages found in bun-global / npm-global");else{info(` found ${discovered.length} @vibecontrols/* package(s) in global roots`);for(let pkg of discovered){let r=uninstallGlobalPlugin(pkg,{dryRun:!!opts.dryRun});if(allRemovals.push(r),opts.dryRun)info(` would uninstall ${colors.bold(pkg.packageName)} (${pkg.root}-global)`);else if(r.ok)success(`Removed ${pkg.packageName} (${pkg.root}-global, via ${r.via})`);else warn(`Failed to remove ${pkg.packageName} (${pkg.root}-global): ${r.error??"unknown"}`)}}let{droppedCount,manifestPath}=cleanBunGlobalManifest((k)=>k.startsWith("@vibecontrols/")&&k!==AGENT_PACKAGE_NAME,!!opts.dryRun);if(droppedCount>0&&!opts.dryRun)success(`Dropped ${droppedCount} @vibecontrols/* dep(s) from ${manifestPath}`);await rmPathSafely(join11(getVibecontrolsProductDir(),"agents.json"),!!opts.dryRun),await rmPathSafely(join11(getVibecontrolsProductDir(),".consent.json"),!!opts.dryRun)}if(allPendingSudo.length>0){blank(),warn(`${allPendingSudo.length} command(s) require sudo to fully uninstall:`);for(let ps of allPendingSudo)kv(` ${ps.name}`,ps.command)}if(maybePrintJson(merged,{ok:!0,deleted:targets.map((t)=>t.profile),removedGlobalPackages:allRemovals,clearedSecrets:secretsResult,dryRun:!!opts.dryRun}))return;if(opts.all&&allRemovals.length>0&&!opts.dryRun){let okCount=allRemovals.filter((r)=>r.ok).length;blank(),info(`${icons.info} Uninstalled ${okCount}/${allRemovals.length} global @vibecontrols/* package(s)`)}if(blank(),success(opts.dryRun?"Dry-run complete.":"Nuke complete."),opts.all&&!opts.dryRun)info(`${icons.info} To remove the agent itself: ${colors.bold("npm uninstall -g @vibecontrols/agent")} (or ${colors.bold("bun remove -g @vibecontrols/agent")})`)}catch(err){let message=err instanceof Error?errMsg(err):String(err);if(merged.json){maybePrintJson(merged,{ok:!1,error:message});return}fail(message)}})}import{existsSync as existsSync11}from"fs";import{platform as platform2}from"os";var DEFAULT_AGENT_URL="http://localhost:3005";function gradeBadge(g){if(g==="pass")return colors.green("\u2714 pass");if(g==="warn")return colors.yellow("\u26A0 warn");return colors.red("\u2718 fail")}async function checkAgentReachable(url){try{return await apiGet(url,"/health"),{name:"Agent HTTP reachable",grade:"pass",message:url}}catch(err){return{name:"Agent HTTP reachable",grade:"fail",message:errMsg(err),hint:"Run `vibe start` (or check `vibe status` if it should already be running)."}}}async function checkConfig(){try{let profile="default";try{profile=getVibecontrolsProfile()}catch{}let fromSecrets=await getSecret(`${profile}:static-api-key`),cfg=readAgentConfig();if(!Boolean(fromSecrets||cfg["static-api-key"]||process.env.AGENT_API_KEY))return{name:"Agent config / API key",grade:"warn",message:"No persistent API key set; agent will mint a fresh one each restart.",hint:"Run `vibe config --set static-api-key=true` to persist."};return{name:"Agent config / API key",grade:"pass",message:"config.json present, API key persisted"}}catch(err){return{name:"Agent config / API key",grade:"fail",message:errMsg(err)}}}function checkAgentDir(){let dir=getVibecontrolsDir();if(!existsSync11(dir))return{name:"Agent working directory",grade:"warn",message:`${dir} does not exist (will be created on first start)`};return{name:"Agent working directory",grade:"pass",message:dir}}async function checkGatewayAuth(url,profile){try{if((await apiGet(url,`/api/profiles/${profile}/agent/gateway-auth`))?.configured)return{name:"Gateway OAuth",grade:"pass",message:"configured"};return{name:"Gateway OAuth",grade:"warn",message:"not configured \u2014 agent works locally but won't sync with platform",hint:"Add the agent on the VibeControls platform and paste the credentials."}}catch{return{name:"Gateway OAuth",grade:"warn",message:"could not query agent for gateway-auth state"}}}async function checkPlugins(url,profile){try{let list=await apiGet(url,`/api/profiles/${profile}/plugins`),all=list?.plugins??list??[],failed=all.filter((p)=>p.status==="error"||p.status==="failed");if(failed.length>0)return{name:"Plugin registry",grade:"fail",message:`${failed.length} plugin(s) failed to load`,hint:failed.map((p)=>p.name??p.packageName).join(", ")};return{name:"Plugin registry",grade:"pass",message:`${all.length} plugin(s) loaded cleanly`}}catch{return{name:"Plugin registry",grade:"warn",message:"could not query agent for plugin list"}}}function checkRuntime(){let os=platform2();if(!["linux","darwin","win32"].includes(os))return{name:"OS runtime",grade:"warn",message:`${os} is not officially supported (linux, darwin, win32)`};let bunVersion=typeof Bun<"u"?Bun.version:null;if(!bunVersion)return{name:"OS runtime",grade:"warn",message:`${os} / no Bun runtime detected`};return{name:"OS runtime",grade:"pass",message:`${os} / Bun ${bunVersion}`}}function register15(program){program.command("doctor").description("Run a graded health check across the agent's subsystems").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL).option("--profile <name>","Target a specific profile by name").option("--json","Emit machine-readable JSON instead of grading text").option("--plain","Force plain text output").action(async function(options){let merged={...program.opts(),...options},url=getAgentUrl(merged),profile=encodeURIComponent(resolveProfileName(merged)),collected=[];if(await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{let checks=[];checks.push(checkRuntime()),checks.push(checkAgentDir()),checks.push(await checkConfig()),checks.push(await checkAgentReachable(url)),checks.push(await checkGatewayAuth(url,profile)),checks.push(await checkPlugins(url,profile));let registry=getActiveContributorRegistry();for(let contributor of registry.getDoctorChecks())try{let results=await contributor.run();for(let r of results){let grade=r.ok?"pass":r.grade??"fail";checks.push({name:r.name,grade,message:r.message,hint:r.hint})}}catch(err){checks.push({name:`${contributor.source}: doctor contributor`,grade:"warn",message:`contributor threw: ${err.message}`})}return collected=checks,checks},plain:(checks)=>{header("Vibe Doctor"),blank();for(let c of checks)if(console.log(`${gradeBadge(c.grade)} ${colors.bold(c.name)}`),console.log(` ${c.message}`),c.hint)console.log(` ${colors.dim("\u2192 "+c.hint)}`);blank();let failed=checks.filter((c)=>c.grade==="fail").length,warned=checks.filter((c)=>c.grade==="warn").length,passed=checks.filter((c)=>c.grade==="pass").length;if(info(`${passed} pass, ${warned} warn, ${failed} fail`),failed>0)fail("Doctor found problems. See hints above."),process.exitCode=1;else if(warned>0)info("Doctor finished with warnings.");else success("All checks pass.")},interactive:async(checks)=>{let rows=checks.map((c,i)=>{let detailLines=[`${gradeBadge(c.grade)} ${colors.bold(c.name)}`,"",c.message];if(c.hint)detailLines.push(""),detailLines.push(colors.dim("\u2192 "+c.hint));return{id:`${i}-${c.name}`,label:c.name,hint:c.grade==="pass"?"ok":c.grade,detail:detailLines.join(`
213
227
  `)}}),failed=checks.filter((c)=>c.grade==="fail").length,warned=checks.filter((c)=>c.grade==="warn").length,passed=checks.filter((c)=>c.grade==="pass").length;if(await interactiveTable({title:`Vibe Doctor \u2014 ${passed} pass, ${warned} warn, ${failed} fail`,rows,footer:"\u2191/\u2193 navigate \xB7 q to quit"}),failed>0)process.exitCode=1},json:(checks)=>({checks:checks.map((c)=>({name:c.name,ok:c.grade==="pass",grade:c.grade,message:c.message,hint:c.hint??null}))})}),collected.some((c)=>c.grade==="fail"))process.exitCode=1})}var DEFAULT_AGENT_URL2="http://localhost:3005",VALID=new Set(["debug","info","warn","error","silent"]);function register16(program){program.command("log-level [level]").description("Show or update the running agent's log level").option("--agent-url <url>","Agent URL",DEFAULT_AGENT_URL2).option("--profile <name>","Target a specific profile by name").option("--json","Emit JSON").option("--plain","Force plain text output").action(async function(level,options){try{let merged={...program.opts(),...options},url=getAgentUrl(merged),profile=encodeURIComponent(resolveProfileName(merged));if(!level){await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>{return{level:(await apiGet(url,`/api/profiles/${profile}/agent/log-level`))?.level??"(unknown)"}},plain:(data)=>{header("Agent Log Level"),kv("Current",data.level)},interactive:async(data)=>{await interactiveDetail({title:"Agent Log Level",body:`Current: ${data.level}`,footer:"press q to exit"})}});return}let target=level.toLowerCase();if(!VALID.has(target)){if(!maybePrintJson(merged,{ok:!1,error:`Invalid log level: ${level}`,valid:[...VALID]}))fail(`Invalid log level: ${level}. Use one of: ${[...VALID].join(", ")}`);return}if(await apiPost(url,`/api/profiles/${profile}/agent/log-level`,{level:target}),maybePrintJson(merged,{ok:!0,level:target}))return;success(`Log level set to ${target}`)}catch(err){if(maybePrintJson({json:!!options.json},{ok:!1,error:errMsg(err)}))return;fail(errMsg(err))}})}import{existsSync as existsSync12,readdirSync as readdirSync3,statSync as statSync3,unlinkSync as unlinkSync5}from"fs";import{join as join12}from"path";function listAgentDirs(){let root=getVibecontrolsProfilesDir();if(!existsSync12(root))return[];return readdirSync3(root).map((n)=>join12(root,n)).filter((p)=>statSync3(p).isDirectory())}function sweepLogs(agentDir,keepDays,dryRun){let logsDir=join12(agentDir,"logs");if(!existsSync12(logsDir))return{count:0,bytes:0};let cutoff=Date.now()-keepDays*86400000,count=0,bytes=0;for(let f of readdirSync3(logsDir)){let p=join12(logsDir,f),s=statSync3(p);if(!s.isFile())continue;if(s.mtimeMs>cutoff)continue;if(count+=1,bytes+=s.size,!dryRun)try{unlinkSync5(p)}catch{}}return{count,bytes}}function sweepTunnelState(agentDir,dryRun){let state=readTunnelState(agentDir);if(!state)return 0;if(isProcessAlive(state.pid))return 0;if(!dryRun)clearTunnelState(agentDir);return 1}function register17(program){program.command("gc").description("Garbage-collect stale agent state (tunnel files, old logs)").option("--keep-days <n>","Keep log files newer than N days","14").option("--dry-run","Show what would be removed without changing anything",!1).option("--json","Emit JSON").action(function(opts){let merged={...program.opts(),...opts};try{let keepDays=Number(opts.keepDays);if(!Number.isFinite(keepDays)||keepDays<0){if(merged.json){maybePrintJson(merged,{ok:!1,error:`Invalid --keep-days: ${opts.keepDays}`});return}fail(`Invalid --keep-days: ${opts.keepDays}`);return}let dryRun=Boolean(opts.dryRun);if(!merged.json)header(dryRun?"vibe gc (dry-run)":"vibe gc"),blank();let stats={staleTunnelStates:0,oldLogFiles:0,bytesFreed:0},removedDirs=[];for(let agentDir of listAgentDirs()){let t=sweepTunnelState(agentDir,dryRun);stats.staleTunnelStates+=t;let logs=sweepLogs(agentDir,keepDays,dryRun);if(stats.oldLogFiles+=logs.count,stats.bytesFreed+=logs.bytes,t>0||logs.count>0)removedDirs.push(agentDir)}if(maybePrintJson(merged,{ok:!0,removed:removedDirs,staleTunnelStates:stats.staleTunnelStates,oldLogFiles:stats.oldLogFiles,freedBytes:stats.bytesFreed,dryRun}))return;if(kv("Stale tunnel state files",String(stats.staleTunnelStates)),kv("Old log files",String(stats.oldLogFiles)),kv("Bytes freed",`${(stats.bytesFreed/1048576).toFixed(2)} MiB`),blank(),dryRun)info(colors.dim("dry-run: no files were removed"));else success("gc complete")}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(errMsg(err))}})}function parseSshTarget(input){let m=/^(?:ssh:\/\/)?(?:([^@\s]+)@)?([^:/\s]+)(?::(\d+))?$/.exec(input.trim());if(!m)throw Error(`Could not parse SSH target: ${input}`);let hasUser=Boolean(m[1]),hasPort=Boolean(m[3]);if(!hasUser&&!hasPort&&!input.includes(":"))return{alias:m[2],host:m[2]};return{user:m[1],host:m[2],port:m[3]?Number(m[3]):void 0}}class SSHTransportImpl{mode="ssh";id;target;constructor(target){if(this.target=target,target.alias)this.id=`ssh://${target.alias}`;else{let userPart=target.user?`${target.user}@`:"",portPart=target.port?`:${target.port}`:"";this.id=`ssh://${userPart}${target.host}${portPart}`}}sshArgv(){let args=["ssh","-o","BatchMode=yes","-o","StrictHostKeyChecking=accept-new","-o","ServerAliveInterval=30"];if(this.target.identityFile)args.push("-i",this.target.identityFile);if(this.target.port)args.push("-p",String(this.target.port));return args.push(this.target.alias??(this.target.user?`${this.target.user}@${this.target.host}`:this.target.host)),args}async probe(){let out={},tasks=[["npm","command -v npm"],["node","command -v node"],["bun","command -v bun"],["vibe","command -v vibe"],["os","uname -s 2>/dev/null || echo unknown"],["arch","uname -m 2>/dev/null || echo unknown"]];for(let[k,cmd]of tasks){let r=await this.run(["sh","-c",cmd]),trimmed=r.stdout.trim();if(r.code===0&&trimmed)out[k]=trimmed}return out}async run(command,opts){let remoteCmd=command.map(quoteForRemoteSh).join(" "),argv=[...this.sshArgv(),remoteCmd],r=Bun.spawnSync(argv,{stdout:"pipe",stderr:"pipe",timeout:opts?.timeoutMs??600000});return{code:r.exitCode??1,stdout:r.stdout.toString(),stderr:r.stderr.toString()}}async upload(remotePath,contents,mode){let argv=[...this.sshArgv(),`cat > ${quoteForRemoteSh(remotePath)} && chmod ${(mode??384).toString(8)} ${quoteForRemoteSh(remotePath)}`],proc=Bun.spawn(argv,{stdin:"pipe",stdout:"pipe",stderr:"pipe"}),writer=proc.stdin;writer.write(typeof contents==="string"?contents:Buffer.from(contents).toString("binary")),await writer.end();let code=await proc.exited;if(code!==0){let err=await new Response(proc.stderr).text();throw Error(`upload to ${remotePath} failed (exit ${code}): ${err}`)}}async close(){}}function quoteForRemoteSh(value){if(/^[A-Za-z0-9._\-/=:]+$/.test(value))return value;return`'${value.replace(/'/g,"'\\''")}'`}function getInstallTransport(target,options){let scheme=/^([a-z]+):\/\//.exec(target)?.[1];if(!scheme||scheme==="ssh"){let parsed=parseSshTarget(target);if(options?.identityFile)parsed.identityFile=options.identityFile;return new SSHTransportImpl(parsed)}throw Error(`Unsupported install transport scheme: ${scheme}://. Supported today: ssh:// (and bare host or ssh_config alias). Future: winrm://, ssm://, docker://, k8s://`)}import{existsSync as existsSync13,mkdirSync as mkdirSync4,readFileSync as readFileSync8,renameSync as renameSync2,writeFileSync as writeFileSync8}from"fs";import{dirname as dirname2,join as join13}from"path";var ALL_PEER_PERMISSIONS=["tunnel:read","session:list","session:share:read","session:share:write","plugin:list","plugin:install","plugin:remove","log:read","diagnostics:read","key:mint","admin"];function registryPath(agentDir){return join13(agentDir??getVibecontrolsDir(),"agent-network.json")}function read(agentDir){let file=registryPath(agentDir);if(!existsSync13(file))return{schemaVersion:1,peers:[]};let raw=readFileSync8(file,"utf8");try{let parsed=JSON.parse(raw);if(!parsed||!Array.isArray(parsed.peers))return{schemaVersion:1,peers:[]};return parsed}catch{return{schemaVersion:1,peers:[]}}}function write(reg,agentDir){let file=registryPath(agentDir);mkdirSync4(dirname2(file),{recursive:!0});let tmp=`${file}.${process.pid}.tmp`;writeFileSync8(tmp,JSON.stringify(reg,null,2),{mode:384}),renameSync2(tmp,file)}function listPeers(agentDir){return read(agentDir).peers}function getPeer(id,agentDir){return read(agentDir).peers.find((p)=>p.id===id)??null}function upsertPeer(record,agentDir){let reg=read(agentDir),i=reg.peers.findIndex((p)=>p.id===record.id);if(i>=0)reg.peers[i]={...reg.peers[i],...record};else reg.peers.push(record);return write(reg,agentDir),record}function updatePeer(id,patch,agentDir){let reg=read(agentDir),i=reg.peers.findIndex((p)=>p.id===id);if(i<0)return null;return reg.peers[i]={...reg.peers[i],...patch},write(reg,agentDir),reg.peers[i]}function removePeer(id,agentDir){let reg=read(agentDir),before=reg.peers.length;if(reg.peers=reg.peers.filter((p)=>p.id!==id),reg.peers.length===before)return!1;return write(reg,agentDir),!0}function sanitizePermissions(requested){let valid=new Set(ALL_PEER_PERMISSIONS);return[...requested].filter((p)=>valid.has(p)).map((p)=>p)}async function doRequest(peer,path,opts){let url=`${peer.tunnelUrl.replace(/\/$/,"")}${path}`;try{let r=await fetch(url,{method:opts.method??"GET",headers:{"x-agent-api-key":peer.apiKey,"content-type":opts.body?"application/json":"application/octet-stream"},body:opts.body?JSON.stringify(opts.body):void 0,signal:AbortSignal.timeout(opts.timeoutMs??30000)});if(r.status===200||r.status===201||r.status===204){let data=await r.json().catch(()=>{return});return{ok:!0,status:r.status,data}}let text=await r.text().catch(()=>"");return{ok:!1,status:r.status,error:text}}catch(err){return{ok:!1,status:0,error:err instanceof Error?err.message:String(err)}}}async function callPeer(peerId,path,opts={}){let peer=getPeer(peerId);if(!peer)return{ok:!1,status:0,error:`No peer with id ${peerId}`};let first=await doRequest(peer,path,opts);if(first.ok)return updatePeer(peer.id,{lastReachable:new Date().toISOString()}),first;if(!(first.status===401||first.status===0||first.status===502||first.status===503||first.status===504))return first;if(!peer.agentRecordId||!gatewayClient.isConfigured())return first;let refreshed=null;try{refreshed=await gatewayClient.lookupPeerAgent(peer.agentRecordId,opts.operation??"peer-call")}catch(err){getDaemonProfile().logger.warn("peer-client","Backend rotation lookup failed",{peerId,error:err instanceof Error?err.message:String(err)})}if(!refreshed)return first;updatePeer(peer.id,{tunnelUrl:refreshed.tunnelUrl,apiKey:refreshed.apiKey});let updated={...peer,tunnelUrl:refreshed.tunnelUrl,apiKey:refreshed.apiKey};return{...await doRequest(updated,path,opts),rotated:!0}}async function runOrFail(transport,cmd,step,timeoutMs){info(`\u2192 ${step}`);let r=await transport.run(cmd,{timeoutMs});if(r.code!==0)throw warn(r.stderr.slice(0,600)),Error(`${step} failed (exit ${r.code})`);return r.stdout}function register18(program){program.command("deploy <target>").description("Install + auto-configure the VibeControls agent on a remote target (defaults to SSH)").option("--version <version>","Pin a specific agent version (default: latest)").option("--registry <url>","npm registry (default: https://registry.npmjs.org/)").option("-i, --identity-file <path>","SSH identity file (override ssh_config)").option("--label <label>","Friendly name to record this peer under (default: target host)").option("--permissions <list>","Permissions to grant ourselves on the new peer (comma-separated)","tunnel:read,session:list,plugin:list,diagnostics:read").option("--expires-in <duration>","TTL for the minted peer key (default: no expiry)").option("--install-plugins <list>","Comma-separated plugin packages to also install on the remote agent").option("--autostart","Run `vibe autostart install` on the remote agent",!1).option("--no-bun","Skip Bun runtime install on the remote (default: best-effort)").option("--confirm","Required to proceed (no destructive ops; this is a sanity gate)").option("--json","Emit JSON").action(async function(target,opts){let merged={...program.opts(),...opts};try{if(!opts.confirm){fail("Pass --confirm to proceed. `vibe deploy` runs npm install on the remote host.");return}let transport=getInstallTransport(target,{identityFile:opts.identityFile});header(`vibe deploy \u2192 ${transport.id}`),blank(),info("Probing remote host\u2026");let probe=await transport.probe();if(kv("Remote OS",probe.os??"(unknown)"),kv("Remote arch",probe.arch??"(unknown)"),probe.npm)kv("Remote npm",probe.npm);if(probe.bun)kv("Remote bun",probe.bun);if(probe.vibe)kv("Remote vibe (existing)",probe.vibe);if(blank(),!probe.npm&&!probe.bun){if(opts.bun===!1){fail("Neither npm nor bun found and --no-bun was passed. Install Node.js manually, then re-run.");return}info("Installing Bun on remote\u2026");let r=await transport.run(["sh","-c",`curl -fsSL https://bun.sh/install | bash && echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.bashrc`],{timeoutMs:300000});if(r.code!==0){warn(r.stderr.slice(0,500)),fail("Bun auto-install failed. Install Node.js or Bun manually, then re-run.");return}let reprobe=await transport.run(["sh","-c","$HOME/.bun/bin/bun --version 2>/dev/null"],{timeoutMs:1e4});if(reprobe.code===0&&reprobe.stdout.trim())probe.bun="$HOME/.bun/bin/bun",kv("Remote bun (installed)",reprobe.stdout.trim());else{fail("Bun installer ran but the binary isn't reachable. Manual investigation needed.");return}}else if(!probe.bun&&opts.bun!==!1)info("Installing Bun on remote (best-effort)\u2026"),await transport.run(["sh","-c","curl -fsSL https://bun.sh/install | bash"],{timeoutMs:300000}).catch(()=>{});let version=opts.version??"latest",registry=opts.registry??"https://registry.npmjs.org/",installer=probe.npm?"npm":"bun";await runOrFail(transport,installer==="npm"?["npm","install","-g",`@vibecontrols/agent@${version}`,`--registry=${registry}`]:["bun","install","-g",`@vibecontrols/agent@${version}`,"--no-cache"],`Installing @vibecontrols/agent@${version} via ${installer}`,600000);let verify=await runOrFail(transport,["sh","-c","vibe --version || ~/.bun/bin/vibe --version"],"Verifying remote vibe binary",15000);if(kv("Remote agent version",verify.trim()),opts.autostart)await runOrFail(transport,["sh","-c","vibe autostart install || ~/.bun/bin/vibe autostart install"],"Installing autostart unit",60000);if(opts.installPlugins)for(let pkg of opts.installPlugins.split(",").map((s)=>s.trim()).filter(Boolean))await runOrFail(transport,["sh","-c",`vibe plugin install ${pkg} || ~/.bun/bin/vibe plugin install ${pkg}`],`Installing plugin ${pkg}`,300000);info("Starting remote agent in daemon mode\u2026"),await transport.run(["sh","-c","vibe start || ~/.bun/bin/vibe start"],{timeoutMs:60000}),info("Waiting for remote tunnel\u2026");let bootstrapJson=null,expFlag=opts.expiresIn?` --expires-in ${opts.expiresIn}`:"",label=opts.label??transport.id,perms=opts.permissions??"tunnel:read";for(let attempt=0;attempt<15;attempt++){let r=await transport.run(["sh","-c",`vibe peer-bootstrap --label '${label.replace(/'/g,"")}' --scope ${perms}${expFlag} 2>/dev/null || ~/.bun/bin/vibe peer-bootstrap --label '${label.replace(/'/g,"")}' --scope ${perms}${expFlag} 2>/dev/null`],{timeoutMs:15000});if(r.code===0){let text=r.stdout,start=text.indexOf("{"),end=text.lastIndexOf("}");if(start>=0&&end>start){let candidate=text.slice(start,end+1);try{JSON.parse(candidate),bootstrapJson=candidate;break}catch{}}}await new Promise((res)=>setTimeout(res,2000))}if(!bootstrapJson){warn("Could not run `vibe peer-bootstrap` on the remote. The agent is installed but not registered as a peer. Run `vibe network add ...` manually with the remote's API key + tunnel URL.");return}let bundle;try{bundle=JSON.parse(bootstrapJson)}catch(err){warn(`Could not parse peer-bootstrap output: ${err.message}`);return}if(!bundle.tunnelUrl)warn("Remote agent has no tunnel URL yet \u2014 it may still be coming up. Re-run `vibe network test <id>` later.");if(upsertPeer({id:bundle.id,label,tunnelUrl:bundle.tunnelUrl??"",apiKey:bundle.apiKey,permissions:bundle.permissions,addedAt:new Date().toISOString(),agentRecordId:bundle.agentRecordId??void 0,workspaceId:bundle.workspaceId??void 0,fingerprint:bundle.id}),maybePrintJson(merged,{ok:!0,host:target,version:verify.trim(),peerId:bundle.id,tunnelUrl:bundle.tunnelUrl??null}))return;if(blank(),success(`Deployed agent + registered peer ${bundle.id}`),kv("Remote tunnel",bundle.tunnelUrl??"(pending)"),kv("Permissions",bundle.permissions.join(",")),bundle.expiresAt)kv("Key expires",bundle.expiresAt);blank(),info(`${colors.dim("Try it")}: ${colors.bold(`vibe network test ${bundle.id.substring(0,8)}`)}`);let probeResult=await callPeer(bundle.id,"/api/profiles/default/agent/identity",{operation:"post-deploy-probe"});if(probeResult.ok)success("Peer probe succeeded.");else warn(`Peer probe failed: ${probeResult.status} ${probeResult.error??""}. Tunnel may still be warming up.`)}catch(err){if(merged.json){maybePrintJson(merged,{ok:!1,error:errMsg(err)});return}fail(errMsg(err))}})}function parsePerms(value){let list=value.split(",").map((s)=>s.trim()).filter(Boolean),sane=sanitizePermissions(list);if(sane.length!==list.length)throw Error(`Invalid permission(s). Allowed: ${ALL_PEER_PERMISSIONS.join(",")}`);return sane}function redactPeer(p){let{apiKey:_apiKey,...rest}=p;return redactUnknownSecrets({...rest,apiKey:"[REDACTED]"})}function register19(program){let cmd=program.command("network").description("Manage the mesh of peer agents this agent talks to");cmd.command("list").description("List configured peer agents").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:()=>listPeers(),plain:(peers)=>{if(peers.length===0){info("No peers configured. Add one via `vibe deploy <ssh-target>` or `vibe network add`.");return}header("Peer agents"),formatTable(peers.map((p)=>({ID:p.id.substring(0,8),Label:p.label,Tunnel:p.tunnelUrl,Permissions:p.permissions.join(","),Last_Reachable:p.lastReachable??"never"})))},interactive:async(peers)=>{if(peers.length===0){header("Peer agents"),blank(),info("No peers configured. Add one via `vibe deploy <ssh-target>` or `vibe network add`.");return}let rows=peers.map((p)=>{let lines=[`${colors.bold(p.label)} ${p.id.substring(0,8)}`,"",` Tunnel: ${p.tunnelUrl}`,` Permissions: ${p.permissions.join(",")}`,` Added: ${p.addedAt}`,` Last reachable: ${p.lastReachable??"never"}`,` Workspace: ${p.workspaceId??"-"}`,` Agent record: ${p.agentRecordId??"-"}`];return{id:p.id,label:p.label,hint:p.lastReachable?"reachable":"never",detail:lines.join(`
214
228
  `)}});await interactiveTable({title:`vibe network \u2014 ${peers.length} peer(s)`,rows})},json:(peers)=>peers.map(redactPeer)})}catch(err){fail(err.message)}}),cmd.command("add <id>").description("Manually add a peer (use deploy for the SSH-install flow)").requiredOption("--label <label>","Friendly name").requiredOption("--tunnel-url <url>","Peer's tunnel URL").requiredOption("--api-key <key>","Scoped API key the peer minted for us").option("--permissions <list>",`Comma-separated permissions (default: tunnel:read). Allowed: ${ALL_PEER_PERMISSIONS.join(",")}`,"tunnel:read").option("--agent-record-id <id>","Platform-side agent ID for backend rotation lookup").option("--workspace-id <id>","Platform-side workspace ID").option("--json","Emit JSON result").action((id,opts)=>{try{let merged={...program.opts(),...opts},perms=parsePerms(opts.permissions);if(upsertPeer({id,label:opts.label,tunnelUrl:opts.tunnelUrl,apiKey:opts.apiKey,permissions:perms,addedAt:new Date().toISOString(),agentRecordId:opts.agentRecordId,workspaceId:opts.workspaceId}),maybePrintJson(merged,{ok:!0,id,label:opts.label,permissions:perms}))return;success(`Added peer ${id} (${opts.label})`)}catch(err){fail(err.message)}}),cmd.command("remove <id>").description("Remove a peer record (does not revoke the key on the peer)").option("--json","Emit JSON result").action((id,opts)=>{let merged={...program.opts(),...opts},ok=removePeer(id);if(maybePrintJson(merged,{ok,id}))return;if(ok)success(`Removed peer ${id}`);else fail(`No peer with id ${id}`)}),cmd.command("update <id>").description("Modify permissions or label of an existing peer").option("--label <label>","Update friendly name").option("--permissions <list>",`Comma-separated permissions. Allowed: ${ALL_PEER_PERMISSIONS.join(",")}`).option("--json","Emit JSON result").action((id,opts)=>{try{let merged={...program.opts(),...opts},patch={};if(opts.label)patch.label=opts.label;if(opts.permissions)patch.permissions=parsePerms(opts.permissions);let updated=updatePeer(id,patch);if(!updated){if(maybePrintJson(merged,{ok:!1,id,error:"not_found"}))return;fail(`No peer with id ${id}`);return}if(maybePrintJson(merged,{ok:!0,id,peer:redactPeer(updated)}))return;success(`Updated peer ${id}`)}catch(err){fail(err.message)}}),cmd.command("test <id>").description("Probe a peer (calls /api/agent/identity over its tunnel)").option("--json","Emit JSON").option("--plain","Force plain text output").action(async(id,opts)=>{let merged={...program.opts(),...opts},peer=getPeer(id);if(!peer){if(maybePrintJson(merged,{ok:!1,id,error:"not_found"}))return;fail(`No peer with id ${id}`);return}try{await runMultimode({mode:pickOutputMode(merged),fetchData:async()=>({peer,result:await callPeer(id,"/api/profiles/default/agent/identity",{operation:"network-test"})}),plain:({peer:p,result:r})=>{if(header(`Probing peer ${p.label} (${p.id})`),blank(),r.ok){if(success(`Peer is reachable${r.rotated?" (recovered via backend rotation lookup)":""}`),r.data)console.log(JSON.stringify(r.data,null,2))}else fail(`Peer call failed (${r.status}): ${r.error??"(no detail)"}`)},interactive:async({peer:p,result:r})=>{let lines=[` Peer: ${p.label} (${p.id})`,` Tunnel: ${p.tunnelUrl}`,` Status: ${r.ok?"reachable":`failed (${r.status})`}`,` Rotated: ${r.rotated?"yes":"no"}`,` Error: ${r.error??"-"}`,"",` Identity: ${r.data?JSON.stringify(r.data,null,2):"-"}`];await interactiveDetail({title:`Probe ${p.label}`,body:lines.join(`
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{__resetDaemonProfileForTests,ensureDaemonProfile,getDaemonProfile,getOrCreateProfile,profileRegistry}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{profileRegistry,getOrCreateProfile,getDaemonProfile,ensureDaemonProfile,__resetDaemonProfileForTests};
2
+ import{__resetDaemonProfileForTests,ensureDaemonProfile,getDaemonProfile,getOrCreateProfile,profileRegistry}from"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";export{profileRegistry,getOrCreateProfile,getDaemonProfile,ensureDaemonProfile,__resetDaemonProfileForTests};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{getDaemonProfile}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";var DEFAULT_BACKOFFS_MS=[300000,600000,900000,1800000];function startFinalizeRetryWorker(opts){let backoffs=opts.backoffsMs??DEFAULT_BACKOFFS_MS,attempt=0,cancelled=!1,nextTimer=null,nextAttemptAt=null,inflight=null,profile=getDaemonProfile();if(profile.getBootState()==="ready")return{cancel(){},async forceRetryNow(){return{ok:!0}},getState(){return{attempt:0,cancelled:!0,nextAttemptAt:null}}};function clearTimer(){if(nextTimer!==null)clearTimeout(nextTimer),nextTimer=null,nextAttemptAt=null}function runOnce(){if(cancelled)return Promise.resolve({ok:!1,error:"retry worker cancelled"});if(profile.getBootState()==="ready")return cancelled=!0,clearTimer(),Promise.resolve({ok:!0});if(inflight)return inflight;return attempt+=1,opts.onAttempt?.(attempt),profile.logger.info("finalize-retry",`Background finalize retry attempt ${attempt}`),inflight=(async()=>{try{let result=await opts.trigger();if(result.ok)profile.logger.info("finalize-retry",`Background finalize succeeded on attempt ${attempt} \u2014 agent recovered`),cancelled=!0,clearTimer();else profile.recordFinalizeError(result.error,attempt),profile.logger.warn("finalize-retry",`Background finalize attempt ${attempt} failed`,{error:result.error});return result}catch(err){let msg=err instanceof Error?err.message:String(err);return profile.recordFinalizeError(msg,attempt),profile.logger.error("finalize-retry",`Background finalize attempt ${attempt} threw`,{error:msg}),{ok:!1,error:msg}}finally{inflight=null}})(),inflight}function scheduleNext(){if(cancelled)return;if(profile.getBootState()==="ready"){cancelled=!0;return}if(nextTimer!==null)return;let idx=Math.min(attempt,backoffs.length-1),delay=backoffs[idx];nextAttemptAt=Date.now()+delay,nextTimer=setTimeout(async()=>{if(nextTimer=null,await runOnce(),!cancelled&&profile.getBootState()!=="ready")scheduleNext()},delay)}return scheduleNext(),{cancel(){cancelled=!0,clearTimer()},async forceRetryNow(){if(profile.getBootState()==="ready")return{ok:!0};if(cancelled)return{ok:!1,error:"retry worker cancelled"};clearTimer();let result=await runOnce();if(!cancelled&&profile.getBootState()!=="ready")scheduleNext();return result},getState(){return{attempt,cancelled,nextAttemptAt:nextAttemptAt?new Date(nextAttemptAt).toISOString():null}}}}export{startFinalizeRetryWorker};
2
+ import{getDaemonProfile}from"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";var DEFAULT_BACKOFFS_MS=[300000,600000,900000,1800000];function startFinalizeRetryWorker(opts){let backoffs=opts.backoffsMs??DEFAULT_BACKOFFS_MS,attempt=0,cancelled=!1,nextTimer=null,nextAttemptAt=null,inflight=null,profile=getDaemonProfile();if(profile.getBootState()==="ready")return{cancel(){},async forceRetryNow(){return{ok:!0}},getState(){return{attempt:0,cancelled:!0,nextAttemptAt:null}}};function clearTimer(){if(nextTimer!==null)clearTimeout(nextTimer),nextTimer=null,nextAttemptAt=null}function runOnce(){if(cancelled)return Promise.resolve({ok:!1,error:"retry worker cancelled"});if(profile.getBootState()==="ready")return cancelled=!0,clearTimer(),Promise.resolve({ok:!0});if(inflight)return inflight;return attempt+=1,opts.onAttempt?.(attempt),profile.logger.info("finalize-retry",`Background finalize retry attempt ${attempt}`),inflight=(async()=>{try{let result=await opts.trigger();if(result.ok)profile.logger.info("finalize-retry",`Background finalize succeeded on attempt ${attempt} \u2014 agent recovered`),cancelled=!0,clearTimer();else profile.recordFinalizeError(result.error,attempt),profile.logger.warn("finalize-retry",`Background finalize attempt ${attempt} failed`,{error:result.error});return result}catch(err){let msg=err instanceof Error?err.message:String(err);return profile.recordFinalizeError(msg,attempt),profile.logger.error("finalize-retry",`Background finalize attempt ${attempt} threw`,{error:msg}),{ok:!1,error:msg}}finally{inflight=null}})(),inflight}function scheduleNext(){if(cancelled)return;if(profile.getBootState()==="ready"){cancelled=!0;return}if(nextTimer!==null)return;let idx=Math.min(attempt,backoffs.length-1),delay=backoffs[idx];nextAttemptAt=Date.now()+delay,nextTimer=setTimeout(async()=>{if(nextTimer=null,await runOnce(),!cancelled&&profile.getBootState()!=="ready")scheduleNext()},delay)}return scheduleNext(),{cancel(){cancelled=!0,clearTimer()},async forceRetryNow(){if(profile.getBootState()==="ready")return{ok:!0};if(cancelled)return{ok:!1,error:"retry worker cancelled"};clearTimer();let result=await runOnce();if(!cancelled&&profile.getBootState()!=="ready")scheduleNext();return result},getState(){return{attempt,cancelled,nextAttemptAt:nextAttemptAt?new Date(nextAttemptAt).toISOString():null}}}}export{startFinalizeRetryWorker};
@@ -1,3 +1,3 @@
1
1
  // @bun
2
- import{recordInstalledPrereqs}from"./index-23rdsqea.js";import{INTERNAL_DISPATCH_HEADER,getInternalDispatchToken}from"./index-d3mz9vws.js";import{getDaemonProfile}from"./index-7pdmqbj8.js";var LOG_SOURCE="prereqs-runner",PLUGIN_STATE_NAMESPACE="plugin-mgr",RESULT_NO_PROTOCOL={status:null,install:null,satisfied:!0,pendingSudo:[],noProtocol:!0};function pluginPrereqPath(plugin,suffix){if(!plugin.apiPrefix)return null;return`${(plugin.apiPrefix.startsWith("/")?plugin.apiPrefix:`/${plugin.apiPrefix}`).replace(/\/+$/,"")}/prereqs${suffix}`}async function callPluginRoute(app,path,method){let handle=app.handle;if(typeof handle!=="function")return null;let req=new Request(`http://agent.local${path}`,{method,headers:{"x-vc-profile-rewrite":"1",[INTERNAL_DISPATCH_HEADER]:getInternalDispatchToken()}}),res=await handle.call(app,req);if(res.status===404)return null;if(!res.ok){let text=await res.text().catch(()=>"");throw Error(`prereqs ${method} ${path} \u2192 ${res.status} ${text}`)}return await res.json()}async function runPluginPrereqs(plugin,packageName,app,db,opts={}){let statusPath=pluginPrereqPath(plugin,"/status"),installPath=pluginPrereqPath(plugin,"/install");if(!statusPath||!installPath)return RESULT_NO_PROTOCOL;let status=null;try{status=await callPluginRoute(app,statusPath,"GET")}catch(err){getDaemonProfile().logger.warn(LOG_SOURCE,`${plugin.name}: prereqs/status call failed`,{error:String(err)})}if(status===null)return RESULT_NO_PROTOCOL;if(status.satisfied||opts.skipInstall)return await persistPrereqState(db,packageName,plugin.version,{status,install:null}),{status,install:null,satisfied:status.satisfied,pendingSudo:[],noProtocol:!1};let install=null;try{install=await callPluginRoute(app,installPath,"POST")}catch(err){getDaemonProfile().logger.warn(LOG_SOURCE,`${plugin.name}: prereqs/install call failed`,{error:String(err)})}let finalStatus=status;try{let refreshed=await callPluginRoute(app,statusPath,"GET");if(refreshed)finalStatus=refreshed}catch{}if(await persistPrereqState(db,packageName,plugin.version,{status:finalStatus,install}),install&&install.installed.length>0){let kindByName=new Map;for(let p of plugin.prerequisites??[])kindByName.set(p.name,p.kind);recordInstalledPrereqs(packageName,install.installed.map((name)=>({name,kind:kindByName.get(name)??"binary"})))}return{status:finalStatus,install,satisfied:finalStatus.satisfied,pendingSudo:install?.pendingSudo??[],noProtocol:!1}}async function persistPrereqState(db,packageName,version,payload){if(!db)return;let value=JSON.stringify({lastInstallAt:new Date().toISOString(),lastInstallVersion:version,satisfied:payload.status?.satisfied??!1,missing:payload.status?.missing??[],pendingSudo:payload.install?.pendingSudo??[]});try{await db.setPluginState(PLUGIN_STATE_NAMESPACE,`prereqs:${packageName}`,value)}catch(err){getDaemonProfile().logger.warn(LOG_SOURCE,"Failed to persist prereq state",{packageName,error:String(err)})}}async function readPrereqState(db,packageName){if(!db)return null;try{let raw=await db.getPluginState(PLUGIN_STATE_NAMESPACE,`prereqs:${packageName}`);return raw?JSON.parse(raw):null}catch{return null}}
2
+ import{recordInstalledPrereqs}from"./index-nd153dk7.js";import{INTERNAL_DISPATCH_HEADER,getInternalDispatchToken}from"./index-d3mz9vws.js";import{getDaemonProfile}from"./index-empz1ez9.js";var LOG_SOURCE="prereqs-runner",PLUGIN_STATE_NAMESPACE="plugin-mgr",RESULT_NO_PROTOCOL={status:null,install:null,satisfied:!0,pendingSudo:[],noProtocol:!0};function pluginPrereqPath(plugin,suffix){if(!plugin.apiPrefix)return null;return`${(plugin.apiPrefix.startsWith("/")?plugin.apiPrefix:`/${plugin.apiPrefix}`).replace(/\/+$/,"")}/prereqs${suffix}`}async function callPluginRoute(app,path,method){let handle=app.handle;if(typeof handle!=="function")return null;let req=new Request(`http://agent.local${path}`,{method,headers:{"x-vc-profile-rewrite":"1",[INTERNAL_DISPATCH_HEADER]:getInternalDispatchToken()}}),res=await handle.call(app,req);if(res.status===404)return null;if(!res.ok){let text=await res.text().catch(()=>"");throw Error(`prereqs ${method} ${path} \u2192 ${res.status} ${text}`)}return await res.json()}async function runPluginPrereqs(plugin,packageName,app,db,opts={}){let statusPath=pluginPrereqPath(plugin,"/status"),installPath=pluginPrereqPath(plugin,"/install");if(!statusPath||!installPath)return RESULT_NO_PROTOCOL;let status=null;try{status=await callPluginRoute(app,statusPath,"GET")}catch(err){getDaemonProfile().logger.warn(LOG_SOURCE,`${plugin.name}: prereqs/status call failed`,{error:String(err)})}if(status===null)return RESULT_NO_PROTOCOL;if(status.satisfied||opts.skipInstall)return await persistPrereqState(db,packageName,plugin.version,{status,install:null}),{status,install:null,satisfied:status.satisfied,pendingSudo:[],noProtocol:!1};let install=null;try{install=await callPluginRoute(app,installPath,"POST")}catch(err){getDaemonProfile().logger.warn(LOG_SOURCE,`${plugin.name}: prereqs/install call failed`,{error:String(err)})}let finalStatus=status;try{let refreshed=await callPluginRoute(app,statusPath,"GET");if(refreshed)finalStatus=refreshed}catch{}if(await persistPrereqState(db,packageName,plugin.version,{status:finalStatus,install}),install&&install.installed.length>0){let kindByName=new Map;for(let p of plugin.prerequisites??[])kindByName.set(p.name,p.kind);recordInstalledPrereqs(packageName,install.installed.map((name)=>({name,kind:kindByName.get(name)??"binary"})))}return{status:finalStatus,install,satisfied:finalStatus.satisfied,pendingSudo:install?.pendingSudo??[],noProtocol:!1}}async function persistPrereqState(db,packageName,version,payload){if(!db)return;let value=JSON.stringify({lastInstallAt:new Date().toISOString(),lastInstallVersion:version,satisfied:payload.status?.satisfied??!1,missing:payload.status?.missing??[],pendingSudo:payload.install?.pendingSudo??[]});try{await db.setPluginState(PLUGIN_STATE_NAMESPACE,`prereqs:${packageName}`,value)}catch(err){getDaemonProfile().logger.warn(LOG_SOURCE,"Failed to persist prereq state",{packageName,error:String(err)})}}async function readPrereqState(db,packageName){if(!db)return null;try{let raw=await db.getPluginState(PLUGIN_STATE_NAMESPACE,`prereqs:${packageName}`);return raw?JSON.parse(raw):null}catch{return null}}
3
3
  export{runPluginPrereqs,readPrereqState};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{getDaemonProfile}from"./index-7pdmqbj8.js";import{getVibecontrolsDir,getVibecontrolsProductDir}from"./index-bysm7taq.js";import{existsSync,mkdirSync,readFileSync,appendFileSync,writeFileSync}from"fs";import{join,relative,resolve,isAbsolute}from"path";var GITIGNORE_LINE=".boff/",GITIGNORE_PATTERNS=new Set([".boff",".boff/","/.boff","/.boff/"]);function bootstrapWorkspace(){mkdirSync(getVibecontrolsDir(),{recursive:!0}),ensureGitignoreEntry()}function ensureGitignoreEntry(){let productDir=resolve(getVibecontrolsProductDir()),cwd=resolve(process.cwd()),rel=relative(cwd,productDir);if(rel.startsWith("..")||isAbsolute(rel))return;let gitignorePath=join(cwd,".gitignore");if(!existsSync(gitignorePath)){writeFileSync(gitignorePath,`${GITIGNORE_LINE}
2
+ import{getDaemonProfile}from"./index-empz1ez9.js";import{getVibecontrolsDir,getVibecontrolsProductDir}from"./index-ef15r9w3.js";import{existsSync,mkdirSync,readFileSync,appendFileSync,writeFileSync}from"fs";import{join,relative,resolve,isAbsolute}from"path";var GITIGNORE_LINE=".boff/",GITIGNORE_PATTERNS=new Set([".boff",".boff/","/.boff","/.boff/"]);function bootstrapWorkspace(){mkdirSync(getVibecontrolsDir(),{recursive:!0}),ensureGitignoreEntry()}function ensureGitignoreEntry(){let productDir=resolve(getVibecontrolsProductDir()),cwd=resolve(process.cwd()),rel=relative(cwd,productDir);if(rel.startsWith("..")||isAbsolute(rel))return;let gitignorePath=join(cwd,".gitignore");if(!existsSync(gitignorePath)){writeFileSync(gitignorePath,`${GITIGNORE_LINE}
3
3
  `,{mode:420}),getDaemonProfile().logger.info("bootstrap",`Created .gitignore with ${GITIGNORE_LINE}`);return}let contents=readFileSync(gitignorePath,"utf8");if(contents.split(/\r?\n/).some((line)=>{let trimmed=line.trim();if(!trimmed||trimmed.startsWith("#"))return!1;return GITIGNORE_PATTERNS.has(trimmed)}))return;let needsLeadingNewline=contents.length>0&&!contents.endsWith(`
4
4
  `);appendFileSync(gitignorePath,`${needsLeadingNewline?`
5
5
  `:""}${GITIGNORE_LINE}
@@ -3,5 +3,5 @@ import{__require}from"./index-e9rt4m94.js";var colorsEnabled=!process.env.NO_COL
3
3
  ${colors.bold(`\u2500\u2500 ${title} \u2500\u2500`)}
4
4
  `)}function kv(key,value){let display=value===null||value===void 0?colors.gray("(none)"):String(value);console.log(` ${colors.bold(key.padEnd(14))} ${display}`)}function blank(){console.log()}function formatTable(rows){if(rows.length===0){console.log(" (none)");return}console.table(rows)}function timeAgo(dateStr){let diff=Date.now()-new Date(dateStr).getTime(),s=Math.floor(diff/1000);if(s<0)return"just now";if(s<60)return`${s}s ago`;let m=Math.floor(s/60);if(m<60)return`${m}m ago`;let h=Math.floor(m/60);if(h<24)return`${h}h ago`;let d=Math.floor(h/24);if(d<30)return`${d}d ago`;return`${Math.floor(d/30)}mo ago`}function formatDuration(seconds){if(seconds<60)return`${Math.floor(seconds)}s`;let m=Math.floor(seconds/60),s=Math.floor(seconds%60);if(m<60)return`${m}m ${s}s`;let h=Math.floor(m/60),rm=m%60;return`${h}h ${rm}m`}function formatBytes(bytes){if(bytes<1024)return`${bytes} B`;if(bytes<1048576)return`${(bytes/1024).toFixed(1)} KB`;if(bytes<1073741824)return`${(bytes/1048576).toFixed(1)} MB`;return`${(bytes/1073741824).toFixed(1)} GB`}function formatStatus(status){switch(status){case"running":case"active":case"healthy":case"success":case"completed":return colors.green(status);case"stopped":case"inactive":case"terminated":case"failed":return colors.red(status);case"starting":case"stopping":case"pending":case"warning":return colors.yellow(status);case"error":return colors.red(status);default:return status}}function shortId(id,len=10){if(id.length<=len)return id;return id.substring(0,len)+"..."}async function printAgentDetails(agentUrl,maxWaitMs=15000){let startTime=Date.now(),healthy=!1;while(Date.now()-startTime<maxWaitMs){try{if((await fetch(`${agentUrl}/health`)).ok){healthy=!0;break}}catch{}await new Promise((r)=>setTimeout(r,500))}if(!healthy){console.log(`
5
5
  ${icons.warning} Agent not yet responding at ${agentUrl}. Check ${colors.bold("vibe logs")} for details.
6
- `);return}let profile=process.env.VIBECONTROLS_PROFILE||"default",tunnelEndpoint=`${agentUrl}/api/profiles/${encodeURIComponent(profile)}/agent/tunnel`,apiKey="(unavailable)";try{let{resolveDefaultApiKey}=await import("./key.cmd-05ckkmn5.js"),resolved=await resolveDefaultApiKey();if(resolved)apiKey=resolved}catch{}let tunnelUrl="(not running)",tunnelWaiting=!1;try{let res=await fetch(tunnelEndpoint);if(res.ok){let data=await res.json();if(data.tunnelUrl)tunnelUrl=data.tunnelUrl;else if(data.status!=="inactive")tunnelWaiting=!0}}catch{}if(tunnelWaiting||tunnelUrl==="(not running)"){console.log(` ${icons.info} Provisioning tunnel (first run downloads the tunnel binary)\u2026`);let tunnelWaitMs=60000,tunnelStart=Date.now();while(Date.now()-tunnelStart<tunnelWaitMs){try{let res=await fetch(tunnelEndpoint);if(res.ok){let data=await res.json();if(data.tunnelUrl){tunnelUrl=data.tunnelUrl;break}}}catch{}await new Promise((r)=>setTimeout(r,1000))}}if(header("Agent Connection Details"),kv("Agent URL:",agentUrl),kv("API Key:",apiKey),kv("Tunnel URL:",tunnelUrl==="(not running)"?colors.yellow(tunnelUrl):colors.green(tunnelUrl)),blank(),console.log(" Copy the API Key and Tunnel URL into the VibeControls UI agent configuration."),tunnelUrl==="(not running)")console.log(` Start a tunnel manually: ${colors.bold(`vibe tunnel agent --start --agent-url ${agentUrl}`)}`);blank()}
6
+ `);return}let profile=process.env.VIBECONTROLS_PROFILE||"default",tunnelEndpoint=`${agentUrl}/api/profiles/${encodeURIComponent(profile)}/agent/tunnel`,apiKey="(unavailable)";try{let{resolveDefaultApiKey}=await import("./key.cmd-eex4j8x7.js"),resolved=await resolveDefaultApiKey();if(resolved)apiKey=resolved}catch{}let tunnelUrl="(not running)",tunnelWaiting=!1;try{let res=await fetch(tunnelEndpoint);if(res.ok){let data=await res.json();if(data.tunnelUrl)tunnelUrl=data.tunnelUrl;else if(data.status!=="inactive")tunnelWaiting=!0}}catch{}if(tunnelWaiting||tunnelUrl==="(not running)"){console.log(` ${icons.info} Provisioning tunnel (first run downloads the tunnel binary)\u2026`);let tunnelWaitMs=60000,tunnelStart=Date.now();while(Date.now()-tunnelStart<tunnelWaitMs){try{let res=await fetch(tunnelEndpoint);if(res.ok){let data=await res.json();if(data.tunnelUrl){tunnelUrl=data.tunnelUrl;break}}}catch{}await new Promise((r)=>setTimeout(r,1000))}}if(header("Agent Connection Details"),kv("Agent URL:",agentUrl),kv("API Key:",apiKey),kv("Tunnel URL:",tunnelUrl==="(not running)"?colors.yellow(tunnelUrl):colors.green(tunnelUrl)),blank(),console.log(" Copy the API Key and Tunnel URL into the VibeControls UI agent configuration."),tunnelUrl==="(not running)")console.log(` Start a tunnel manually: ${colors.bold(`vibe tunnel agent --start --agent-url ${agentUrl}`)}`);blank()}
7
7
  export{setColorsEnabled,colors,icons,fail,errMsg,success,warn,info,header,kv,blank,formatTable,timeAgo,formatDuration,formatBytes,formatStatus,shortId,printAgentDetails};
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{getSecret,readAgentConfig}from"./index-2pqv0bya.js";import{getVibecontrolsProductDir,getVibecontrolsProfile}from"./index-bysm7taq.js";function isInteractive(){return!!process.stdout.isTTY&&!!process.stdin.isTTY}async function promptConfirm(message,defaultValue=!1){if(!isInteractive())return defaultValue;let hint=defaultValue?"[Y/n]":"[y/N]";return process.stdout.write(` ${message} ${hint} `),new Promise((resolve)=>{let stdin=process.stdin;stdin.setRawMode?.(!0),stdin.resume(),stdin.setEncoding("utf8");let onData=(key)=>{stdin.removeListener("data",onData),stdin.setRawMode?.(!1),stdin.pause();let answer=key.trim().toLowerCase();if(console.log(answer||(defaultValue?"y":"n")),answer==="")resolve(defaultValue);else resolve(answer==="y")};stdin.on("data",onData)})}function pickOutputMode(flags){if(flags.json)return"json";if(flags.plain)return"plain";if(flags.interactive)return"interactive";return"auto"}function isCi(){return!!process.env.CI||!!process.env.NO_COLOR||process.env.TERM==="dumb"}async function runMultimode(opts){let data=await opts.fetchData(),mode=opts.mode??"auto";if(mode==="json"){let shaped=opts.json?opts.json(data):data;process.stdout.write(`${JSON.stringify(shaped,null,2)}
2
+ import{getSecret,readAgentConfig}from"./index-b0arfgtz.js";import{getVibecontrolsProductDir,getVibecontrolsProfile}from"./index-ef15r9w3.js";function isInteractive(){return!!process.stdout.isTTY&&!!process.stdin.isTTY}async function promptConfirm(message,defaultValue=!1){if(!isInteractive())return defaultValue;let hint=defaultValue?"[Y/n]":"[y/N]";return process.stdout.write(` ${message} ${hint} `),new Promise((resolve)=>{let stdin=process.stdin;stdin.setRawMode?.(!0),stdin.resume(),stdin.setEncoding("utf8");let onData=(key)=>{stdin.removeListener("data",onData),stdin.setRawMode?.(!1),stdin.pause();let answer=key.trim().toLowerCase();if(console.log(answer||(defaultValue?"y":"n")),answer==="")resolve(defaultValue);else resolve(answer==="y")};stdin.on("data",onData)})}function pickOutputMode(flags){if(flags.json)return"json";if(flags.plain)return"plain";if(flags.interactive)return"interactive";return"auto"}function isCi(){return!!process.env.CI||!!process.env.NO_COLOR||process.env.TERM==="dumb"}async function runMultimode(opts){let data=await opts.fetchData(),mode=opts.mode??"auto";if(mode==="json"){let shaped=opts.json?opts.json(data):data;process.stdout.write(`${JSON.stringify(shaped,null,2)}
3
3
  `);return}if(mode==="plain"){await opts.plain(data);return}if((mode==="interactive"||isInteractive()&&!isCi())&&!!opts.interactive&&opts.interactive)try{await opts.interactive(data);return}catch{}await opts.plain(data)}function maybePrintJson(flags,data){if(!flags.json)return!1;return process.stdout.write(`${JSON.stringify(data,null,2)}
4
4
  `),!0}import{existsSync,readFileSync}from"fs";import{join}from"path";var DEFAULT_AGENT_URL="http://localhost:3005",LOCAL_AGENT_HOSTS=new Set(["localhost","127.0.0.1","0.0.0.0","::1","[::1]"]);function normalizeHost(host){return host.toLowerCase().replace(/^\[|\]$/g,"")}function isLocalAgentUrl(agentUrl){try{let url=new URL(agentUrl);return LOCAL_AGENT_HOSTS.has(normalizeHost(url.hostname))}catch{return!1}}function normalizeAgentUrl(agentUrl){let url=new URL(agentUrl.trim());if(url.protocol!=="http:"&&url.protocol!=="https:")throw Error("Agent URL must use http or https");if(url.username||url.password)throw Error("Agent URL must not include credentials");let normalized=url.toString().replace(/\/+$/,"");if(!isLocalAgentUrl(normalized)&&process.env.VIBECONTROLS_ALLOW_REMOTE_AGENT_URL!=="1")throw Error("Refusing non-local agent URL. Set VIBECONTROLS_ALLOW_REMOTE_AGENT_URL=1 to target a remote/tunnel URL explicitly.");return normalized}function isPidAlive(pid){if(!Number.isInteger(pid)||pid<=0)return!1;try{return process.kill(pid,0),!0}catch{return!1}}function readRegistryEntries(){let registryPath=join(getVibecontrolsProductDir(),"agents.json");if(!existsSync(registryPath))return[];try{let raw=JSON.parse(readFileSync(registryPath,"utf-8"));return(Array.isArray(raw)?raw:raw&&typeof raw==="object"&&Array.isArray(raw.instances)?raw.instances:[]).filter((e)=>!!e&&typeof e==="object"&&typeof e.name==="string"&&typeof e.port==="number")}catch{return[]}}function resolveAgentUrlByName(name){let agent=readRegistryEntries().find((entry)=>entry.name===name);if(!agent||!agent.port)return null;if(typeof agent.pid==="number"&&!isPidAlive(agent.pid))return null;return{url:`http://localhost:${agent.port}`,port:agent.port}}function getAgentUrl(opts){let explicitAgentUrl=opts?.agentUrl&&opts.agentUrl!==DEFAULT_AGENT_URL?opts.agentUrl:void 0;if(explicitAgentUrl)return normalizeAgentUrl(explicitAgentUrl);if(opts?.profile){let resolved=resolveAgentUrlByName(opts.profile);if(resolved)return resolved.url}if(process.env.AGENT_URL)return normalizeAgentUrl(process.env.AGENT_URL);try{let defaultName=resolveProfileName({}),resolved=resolveAgentUrlByName(defaultName);if(resolved)return resolved.url}catch{}return normalizeAgentUrl(DEFAULT_AGENT_URL)}async function resolveApiKey(agentUrl){let local=isLocalAgentUrl(agentUrl);if(process.env.AGENT_API_KEY){if(local||process.env.VIBECONTROLS_SEND_API_KEY_TO_REMOTE==="1")return process.env.AGENT_API_KEY;throw Error("Refusing to send AGENT_API_KEY to a non-local agent URL. Set VIBECONTROLS_SEND_API_KEY_TO_REMOTE=1 only if you trust the target.")}if(!local)return;let fromDaemon=await fetchApiKeyFromDaemon(agentUrl);if(fromDaemon)return fromDaemon;let profile=getVibecontrolsProfile(),fromSecrets=await getSecret(`${profile}:static-api-key`);if(fromSecrets)return fromSecrets;return readAgentConfig()["static-api-key"]}async function fetchApiKeyFromDaemon(agentUrl){try{let profile=encodeURIComponent(getVibecontrolsProfile()),controller=new AbortController,timer=setTimeout(()=>controller.abort(),2000);try{let res=await fetch(`${agentUrl}/api/profiles/${profile}/agent/api-key`,{signal:controller.signal});if(!res.ok)return;return(await res.json().catch(()=>null))?.apiKey||void 0}finally{clearTimeout(timer)}}catch{return}}function resolveProfileName(opts){if(opts?.profile)return opts.profile;if(process.env.VIBECONTROLS_PROFILE)return process.env.VIBECONTROLS_PROFILE;try{let registryPath=join(getVibecontrolsProductDir(),"agents.json");if(existsSync(registryPath)){let raw=JSON.parse(readFileSync(registryPath,"utf-8")),def=raw&&typeof raw==="object"&&!Array.isArray(raw)?raw.defaultProfile:void 0;if(typeof def==="string"&&def.length>0)return def}}catch{}return"default"}async function agentFetch(agentUrl,path,options={}){let apiKey=await resolveApiKey(agentUrl),headers={...options.body?{"Content-Type":"application/json"}:{},...apiKey?{"x-agent-api-key":apiKey}:{},...options.headers??{}},res;try{res=await fetch(`${agentUrl}${path}`,{...options,headers})}catch(err){let msg=err instanceof Error?err.message:String(err),isConnError=/unable to connect|ECONNREFUSED|ENOTFOUND|EHOSTUNREACH|fetch failed/i.test(msg);if(isConnError&&isLocalAgentUrl(agentUrl))throw Error(`Agent not reachable at ${agentUrl}. Is it running? Try 'vibe status' or 'vibe start'.`,{cause:err});if(isConnError)throw Error(`Agent not reachable at ${agentUrl}. ${msg}`,{cause:err});throw err}let data=await res.json().catch(()=>({}));return{ok:res.ok,status:res.status,data}}async function apiGet(agentUrl,path){let{ok,status,data}=await agentFetch(agentUrl,path);if(!ok){let errorMsg=data?.error||data?.message||`Agent returned ${status}`;throw Error(String(errorMsg))}return data}async function apiPost(agentUrl,path,body){let{ok,status,data}=await agentFetch(agentUrl,path,{method:"POST",body:body!=null?JSON.stringify(body):void 0});if(!ok){let errorMsg=data?.error||data?.message||`Agent returned ${status}`;throw Error(String(errorMsg))}return data}async function apiPut(agentUrl,path,body){let{ok,status,data}=await agentFetch(agentUrl,path,{method:"PUT",body:body!=null?JSON.stringify(body):void 0});if(!ok){let errorMsg=data?.error||data?.message||`Agent returned ${status}`;throw Error(String(errorMsg))}return data}async function apiDelete(agentUrl,path){let{ok,status,data}=await agentFetch(agentUrl,path,{method:"DELETE"});if(!ok){let errorMsg=data?.error||data?.message||`Agent returned ${status}`;throw Error(String(errorMsg))}return data}export{isLocalAgentUrl,getAgentUrl,resolveApiKey,resolveProfileName,apiGet,apiPost,apiPut,apiDelete,isInteractive,promptConfirm,pickOutputMode,runMultimode,maybePrintJson};
@@ -1,5 +1,5 @@
1
1
  // @bun
2
- import{Elysia}from"./index-rnk0kny8.js";import{apiGet,getAgentUrl,resolveApiKey,resolveProfileName}from"./index-wvabz3xh.js";import{blank,colors,errMsg,fail,header,info,timeAgo}from"./index-r0qezdp7.js";import"./index-campp0wv.js";import"./index-2pqv0bya.js";import{getDaemonProfile}from"./index-7pdmqbj8.js";import"./index-bysm7taq.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";var LEVEL_PRIORITY={debug:0,info:1,warn:2,error:3};function parseLevel(level){if(!level)return;return level==="debug"||level==="info"||level==="warn"||level==="error"?level:void 0}function parseBoundedInt(value,fallback,min,max){if(!value)return fallback;let parsed=parseInt(value,10);if(!Number.isInteger(parsed))return fallback;return Math.min(max,Math.max(min,parsed))}function createRoutes(_deps){return new Elysia().get("/",async({query,set})=>{let q=query,level=parseLevel(q.level);if(q.level&&!level)return set.status=400,{error:"Invalid log level"};let entries=await getDaemonProfile().logger.readHistory({level,source:q.source,from:q.from,to:q.to,limit:parseBoundedInt(q.limit,200,1,1000),offset:parseBoundedInt(q.offset,0,0,1e5)});return{entries,count:entries.length}}).get("/stream",({query,set})=>{let q=query,parsedLevel=parseLevel(q.level);if(q.level&&!parsedLevel)return set.status=400,{error:"Invalid log level"};let minLevel=parsedLevel??"info",sourceFilter=q.source??"";return set.headers["Content-Type"]="text/event-stream",set.headers["Cache-Control"]="no-cache",set.headers.Connection="keep-alive",set.headers["X-Accel-Buffering"]="no",new ReadableStream({start(controller){let encoder=new TextEncoder;controller.enqueue(encoder.encode(`: connected
2
+ import{Elysia}from"./index-rnk0kny8.js";import{apiGet,getAgentUrl,resolveApiKey,resolveProfileName}from"./index-44trf1k5.js";import{blank,colors,errMsg,fail,header,info,timeAgo}from"./index-3y6z75rj.js";import"./index-campp0wv.js";import"./index-b0arfgtz.js";import{getDaemonProfile}from"./index-empz1ez9.js";import"./index-ef15r9w3.js";import"./index-zs58d1hc.js";import"./index-e9rt4m94.js";var LEVEL_PRIORITY={debug:0,info:1,warn:2,error:3};function parseLevel(level){if(!level)return;return level==="debug"||level==="info"||level==="warn"||level==="error"?level:void 0}function parseBoundedInt(value,fallback,min,max){if(!value)return fallback;let parsed=parseInt(value,10);if(!Number.isInteger(parsed))return fallback;return Math.min(max,Math.max(min,parsed))}function createRoutes(_deps){return new Elysia().get("/",async({query,set})=>{let q=query,level=parseLevel(q.level);if(q.level&&!level)return set.status=400,{error:"Invalid log level"};let entries=await getDaemonProfile().logger.readHistory({level,source:q.source,from:q.from,to:q.to,limit:parseBoundedInt(q.limit,200,1,1000),offset:parseBoundedInt(q.offset,0,0,1e5)});return{entries,count:entries.length}}).get("/stream",({query,set})=>{let q=query,parsedLevel=parseLevel(q.level);if(q.level&&!parsedLevel)return set.status=400,{error:"Invalid log level"};let minLevel=parsedLevel??"info",sourceFilter=q.source??"";return set.headers["Content-Type"]="text/event-stream",set.headers["Cache-Control"]="no-cache",set.headers.Connection="keep-alive",set.headers["X-Accel-Buffering"]="no",new ReadableStream({start(controller){let encoder=new TextEncoder;controller.enqueue(encoder.encode(`: connected
3
3
 
4
4
  `));let onLog=(entry)=>{if(LEVEL_PRIORITY[entry.level]<LEVEL_PRIORITY[minLevel])return;if(sourceFilter&&!entry.source.includes(sourceFilter))return;try{controller.enqueue(encoder.encode(`event: log
5
5
  data: ${JSON.stringify(entry)}