@visulima/vis 1.0.0-alpha.10 → 1.0.0-alpha.11

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 (120) hide show
  1. package/CHANGELOG.md +95 -42
  2. package/LICENSE.md +213 -0
  3. package/README.md +8 -4
  4. package/dist/bin.js +9 -1
  5. package/dist/config/index.d.ts +1818 -0
  6. package/dist/config/index.js +2 -0
  7. package/dist/generate/index.d.ts +1 -1
  8. package/dist/generate/index.js +3 -1
  9. package/dist/packem_chunks/applyDefaults.js +336 -0
  10. package/dist/packem_chunks/bin.js +9554 -64
  11. package/dist/packem_chunks/doctor-probe.js +112 -0
  12. package/dist/packem_chunks/fix.js +229 -48
  13. package/dist/packem_chunks/handler.js +99 -1
  14. package/dist/packem_chunks/handler10.js +53 -1
  15. package/dist/packem_chunks/handler11.js +32 -1
  16. package/dist/packem_chunks/handler12.js +100 -2
  17. package/dist/packem_chunks/handler13.js +25 -1
  18. package/dist/packem_chunks/handler14.js +916 -5
  19. package/dist/packem_chunks/handler15.js +206 -1
  20. package/dist/packem_chunks/handler16.js +122 -18
  21. package/dist/packem_chunks/handler17.js +13 -1
  22. package/dist/packem_chunks/handler18.js +106 -1
  23. package/dist/packem_chunks/handler19.js +19 -1
  24. package/dist/packem_chunks/handler2.js +75 -1
  25. package/dist/packem_chunks/handler20.js +29 -1
  26. package/dist/packem_chunks/handler21.js +222 -1
  27. package/dist/packem_chunks/handler22.js +237 -5
  28. package/dist/packem_chunks/handler23.js +101 -1
  29. package/dist/packem_chunks/handler24.js +110 -1
  30. package/dist/packem_chunks/handler25.js +402 -5
  31. package/dist/packem_chunks/handler26.js +13 -1
  32. package/dist/packem_chunks/handler27.js +63 -3
  33. package/dist/packem_chunks/handler28.js +34 -1
  34. package/dist/packem_chunks/handler29.js +458 -7
  35. package/dist/packem_chunks/handler3.js +95 -2
  36. package/dist/packem_chunks/handler30.js +168 -21
  37. package/dist/packem_chunks/handler31.js +530 -3
  38. package/dist/packem_chunks/handler32.js +214 -2
  39. package/dist/packem_chunks/handler33.js +119 -24
  40. package/dist/packem_chunks/handler34.js +630 -2
  41. package/dist/packem_chunks/handler35.js +283 -19
  42. package/dist/packem_chunks/handler36.js +521 -407
  43. package/dist/packem_chunks/handler37.js +762 -22
  44. package/dist/packem_chunks/handler38.js +989 -22
  45. package/dist/packem_chunks/handler39.js +574 -22
  46. package/dist/packem_chunks/handler4.js +90 -4
  47. package/dist/packem_chunks/handler40.js +1685 -3
  48. package/dist/packem_chunks/handler41.js +1088 -10
  49. package/dist/packem_chunks/handler42.js +785 -141
  50. package/dist/packem_chunks/handler43.js +2658 -42
  51. package/dist/packem_chunks/handler44.js +3886 -3
  52. package/dist/packem_chunks/handler45.js +2568 -21
  53. package/dist/packem_chunks/handler46.js +3769 -0
  54. package/dist/packem_chunks/handler47.js +1491 -0
  55. package/dist/packem_chunks/handler5.js +174 -2
  56. package/dist/packem_chunks/handler6.js +95 -13
  57. package/dist/packem_chunks/handler7.js +115 -8
  58. package/dist/packem_chunks/handler8.js +12 -1
  59. package/dist/packem_chunks/handler9.js +29 -1
  60. package/dist/packem_chunks/heal-accept.js +522 -0
  61. package/dist/packem_chunks/heal.js +673 -0
  62. package/dist/packem_chunks/index.js +873 -7
  63. package/dist/packem_chunks/loader.js +23 -1
  64. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +1316 -0
  65. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +5 -0
  66. package/dist/packem_shared/ai-analysis-CHeB1joD.js +367 -0
  67. package/dist/packem_shared/ai-cache-Be_jexe4.js +142 -0
  68. package/dist/packem_shared/ai-fix-B9iQVcD2.js +379 -0
  69. package/dist/packem_shared/cache-directory-2qvs4goY.js +98 -0
  70. package/dist/packem_shared/catalog-BJTtyi-O.js +1371 -0
  71. package/dist/packem_shared/dependency-scan-A0KSklpG.js +188 -0
  72. package/dist/packem_shared/docker-2iZzc280.js +181 -0
  73. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +100 -0
  74. package/dist/packem_shared/flakiness-goTxXuCX.js +180 -0
  75. package/dist/packem_shared/otel-DCvqCTz_.js +158 -0
  76. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +3 -0
  77. package/dist/packem_shared/registry-CbqXI0rc.js +272 -0
  78. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +130 -0
  79. package/dist/packem_shared/runtime-check-Cobi3p6l.js +127 -0
  80. package/dist/packem_shared/selectors-SM69TfqC.js +194 -0
  81. package/dist/packem_shared/symbols-Ta7g2nU-.js +14 -0
  82. package/dist/packem_shared/toolchain-BdZd9eBi.js +975 -0
  83. package/dist/packem_shared/typosquats-C-bCh3PX.js +1210 -0
  84. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +20 -0
  85. package/dist/packem_shared/utils-CthVdBPS.js +40 -0
  86. package/dist/packem_shared/xxh3-Ck8mXNg1.js +239 -0
  87. package/index.js +727 -555
  88. package/package.json +35 -17
  89. package/schemas/project.schema.json +8 -10
  90. package/schemas/vis-config.schema.json +132 -8
  91. package/skills/vis/SKILL.md +96 -0
  92. package/templates/buildkite-ci/.buildkite/pipeline.yml.tera +85 -0
  93. package/templates/buildkite-ci/template.yml +20 -0
  94. package/dist/errors/index.d.ts +0 -26
  95. package/dist/errors/index.js +0 -1
  96. package/dist/packem_chunks/config.js +0 -2
  97. package/dist/packem_shared/VisConfigCycleError-CAYNC7d-.js +0 -1
  98. package/dist/packem_shared/VisConfigError-B5LP1zRf.js +0 -1
  99. package/dist/packem_shared/VisConfigLoadError-CeqBSd2Z.js +0 -2
  100. package/dist/packem_shared/VisConfigNotFoundError-DZ9KC527.js +0 -5
  101. package/dist/packem_shared/VisUpdateApp-D-L4_-Iu.js +0 -1
  102. package/dist/packem_shared/_commonjsHelpers-D6W6KoPK.js +0 -1
  103. package/dist/packem_shared/ai-analysis-CGuy7dfE.js +0 -67
  104. package/dist/packem_shared/ai-cache-Bynt6Y9x.js +0 -1
  105. package/dist/packem_shared/cache-directory-D72ZEag2.js +0 -1
  106. package/dist/packem_shared/catalog-BVPerCwG.js +0 -12
  107. package/dist/packem_shared/dependency-scan-Du0tBu64.js +0 -2
  108. package/dist/packem_shared/docker-BcfqH4Av.js +0 -2
  109. package/dist/packem_shared/failure-log-DqYen0LC.js +0 -2
  110. package/dist/packem_shared/flakiness-DSIHZGBT.js +0 -1
  111. package/dist/packem_shared/run-summary-utils-C24Aaf9E.js +0 -1
  112. package/dist/packem_shared/runtime-check-CGHal8SO.js +0 -1
  113. package/dist/packem_shared/selectors-CfH9ZY08.js +0 -3
  114. package/dist/packem_shared/symbols-CQmER5MT.js +0 -1
  115. package/dist/packem_shared/target-merge-DNa-6eWu.js +0 -1
  116. package/dist/packem_shared/toolchain-DQfTQY8E.js +0 -5
  117. package/dist/packem_shared/typosquats-DOR8izpX.js +0 -1
  118. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +0 -1
  119. package/dist/packem_shared/utils-DrNg0XTR.js +0 -1
  120. package/dist/packem_shared/xxh3-DrAUNq4n.js +0 -1
@@ -1,428 +1,542 @@
1
- var oe=Object.defineProperty;var E=(o,e)=>oe(o,"name",{value:e,configurable:!0});import{createRequire as ie}from"node:module";import{dim as k,bold as S,cyan as z}from"@visulima/colorize";import{projectGraphToDot as ce}from"@visulima/task-runner";import{Box as i,Text as t,ScrollView as ae,ScrollBar as pe,useApp as he,useWindowSize as ue,useInput as fe,Dialog as ge,render as me}from"@visulima/tui";import{M as W,r as xe,s as be,d as ye}from"./bin.js";import we,{useSyncExternalStore as ve,useState as O,useRef as q,useMemo as _,useCallback as U,useEffect as Ce}from"react";import{jsx as n,jsxs as r}from"react/jsx-runtime";const le=ie(import.meta.url),$=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,de=E(o=>{if(typeof $<"u"&&$.versions&&$.versions.node){const[e,l]=$.versions.node.split(".").map(Number);if(e>22||e===22&&l>=3||e===20&&l>=16)return $.getBuiltinModule(o)}return le(o)},"__cjs_getBuiltinModule"),{writeFileSync:se}=de("node:fs");var Ae=Object.defineProperty,I=E((o,e)=>Ae(o,"name",{value:e,configurable:!0}),"n");const ke=I(o=>{const e=new Map;for(const[l,d]of Object.entries(o.dependencies))for(const c of d){const s=e.get(c.target)??[];s.push(l),e.set(c.target,s)}return Object.values(o.nodes).map(l=>({deps:o.dependencies[l.name]??[],name:l.name,reverseDeps:e.get(l.name)??[],type:l.type})).sort((l,d)=>l.type!==d.type?l.type==="application"?-1:1:l.name.localeCompare(d.name))},"buildNodes"),Ee=I((o,e,l)=>{let d=o;if(e==="app"?d=d.filter(c=>c.type==="application"):e==="lib"&&(d=d.filter(c=>c.type!=="application")),l){const c=l.toLowerCase();d=d.filter(s=>s.name.toLowerCase().includes(c))}return d},"filterNodes");class Me{static{E(this,"GraphStore")}static{I(this,"GraphStore")}#e;#n=new Set;#r;constructor(e){this.#r=e;const l=ke(e);this.#e={allNodes:l,filterActive:!1,filterText:"",filterType:"all",focusedPanel:"list",selectedIndex:0}}getSnapshot=I(()=>this.#e,"getSnapshot");subscribe=I(e=>(this.#n.add(e),()=>{this.#n.delete(e)}),"subscribe");getFilteredNodes(){return Ee(this.#e.allNodes,this.#e.filterType,this.#e.filterText)}getStats(){const e=this.#e.allNodes.filter(c=>c.type==="application").length,l=this.#e.allNodes.length,d=Object.values(this.#r.dependencies).reduce((c,s)=>c+s.length,0);return{apps:e,deps:d,libs:l-e,total:l}}setSelectedIndex(e){const l=this.getFilteredNodes(),d=l.length===0?-1:Math.max(0,Math.min(e,l.length-1));d!==this.#e.selectedIndex&&this.#t({...this.#e,selectedIndex:d})}setFocusedPanel(e){e!==this.#e.focusedPanel&&this.#t({...this.#e,focusedPanel:e})}setFilterType(e){e!==this.#e.filterType&&this.#t({...this.#e,filterType:e,selectedIndex:0})}setFilter(e){this.#t({...this.#e,filterText:e,selectedIndex:0})}setFilterActive(e){e!==this.#e.filterActive&&this.#t({...this.#e,filterActive:e,filterText:e?this.#e.filterText:"",selectedIndex:e?this.#e.selectedIndex:0})}#t(e){this.#e=e;for(const l of this.#n)try{l()}catch{}}}var Te=Object.defineProperty,Se=E((o,e)=>Te(o,"name",{value:e,configurable:!0}),"p");const je=Se(({focused:o,node:e,scrollRef:l})=>{const d=o?"white":"gray";if(!e)return n(i,{alignItems:"center",borderColor:"gray",borderStyle:"single",flexDirection:"column",flexGrow:1,justifyContent:"center",children:n(t,{dimColor:!0,children:"No project selected"})});const c=e.type==="application",s=c?"yellow":"cyan",u=c?"Application":"Library";return n(i,{borderColor:d,borderStyle:"single",borderTopRightTitle:` ${u} `,borderTopTitle:` ${e.name} `,flexDirection:"column",flexGrow:1,children:r(ae,{flexGrow:1,flexShrink:1,paddingX:2,ref:l,scrollbar:!0,scrollbarColor:"gray",children:[r(i,{flexDirection:"column",marginTop:1,children:[r(i,{children:[n(t,{dimColor:!0,children:"── "}),n(t,{bold:!0,color:"white",children:"DEPENDS ON"}),r(t,{dimColor:!0,children:[" ","(",e.deps.length,")"]})]}),e.deps.length===0?n(i,{marginTop:1,paddingLeft:2,children:n(t,{dimColor:!0,children:"No dependencies"})}):n(i,{flexDirection:"column",marginTop:1,children:e.deps.map(a=>r(i,{gap:1,paddingLeft:2,children:[n(t,{color:"cyan",children:"→"}),n(t,{children:a.target}),a.type!=="static"&&r(t,{dimColor:!0,children:["(",a.type,")"]})]},a.target))})]}),r(i,{flexDirection:"column",marginTop:1,children:[r(i,{children:[n(t,{dimColor:!0,children:"── "}),n(t,{bold:!0,color:"white",children:"REQUIRED BY"}),r(t,{dimColor:!0,children:[" ","(",e.reverseDeps.length,")"]})]}),e.reverseDeps.length===0?n(i,{marginTop:1,paddingLeft:2,children:n(t,{dimColor:!0,children:"No reverse dependencies"})}):n(i,{flexDirection:"column",marginTop:1,children:e.reverseDeps.map(a=>r(i,{gap:1,paddingLeft:2,children:[n(t,{color:"magenta",children:"←"}),n(t,{children:a})]},a))})]}),r(i,{flexDirection:"column",marginTop:1,children:[r(i,{children:[n(t,{dimColor:!0,children:"── "}),n(t,{bold:!0,color:"white",children:"INFO"})]}),r(i,{flexDirection:"column",marginTop:1,paddingLeft:2,children:[r(i,{children:[n(i,{width:16,children:n(t,{dimColor:!0,children:"Type:"})}),n(t,{color:s,children:u})]}),r(i,{children:[n(i,{width:16,children:n(t,{dimColor:!0,children:"Dependencies:"})}),n(t,{children:String(e.deps.length)})]}),r(i,{children:[n(i,{width:16,children:n(t,{dimColor:!0,children:"Required by:"})}),n(t,{children:String(e.reverseDeps.length)})]}),r(i,{children:[n(i,{width:16,children:n(t,{dimColor:!0,children:"Connectivity:"})}),n(t,{children:String(e.deps.length+e.reverseDeps.length)})]})]})]})]})})},"ProjectDetailPanel");var De=Object.defineProperty,B=E((o,e)=>De(o,"name",{value:e,configurable:!0}),"g");const Ne=[{key:"all",label:"All",shortcut:"1"},{key:"app",label:"Apps",shortcut:"2"},{key:"lib",label:"Libs",shortcut:"3"}],Y=B(({isSelected:o,node:e})=>{const l=e.type==="application",d=l?"yellow":"cyan",c=l?"app":"lib";return r(i,{flexShrink:0,height:1,children:[r(t,{children:[o?"▶":" "," "]}),n(i,{flexGrow:1,children:n(t,{bold:o,inverse:o,wrap:"truncate",children:e.name})}),r(t,{color:d,children:[" ",c]}),r(t,{dimColor:!0,children:[" ","→",e.deps.length," ","←",e.reverseDeps.length]})]})},"ProjectRow"),X=B(({count:o,label:e})=>r(i,{flexShrink:0,height:1,marginTop:1,children:[r(t,{dimColor:!0,children:["▼"," "]}),n(t,{bold:!0,color:"white",children:e.toUpperCase()}),r(t,{dimColor:!0,children:[" ","(",o,")"]})]}),"TypeHeader"),$e=B(({filterActive:o,filterText:e,filterType:l,focused:d,nodes:c,scrollOffset:s,selectedIndex:u,stats:a,viewportHeight:y})=>{const g=d?"white":"gray",v=c.filter(w=>w.type==="application"),h=c.filter(w=>w.type!=="application"),f=[];let x=0;if(v.length>0){f.push(n(X,{count:v.length,label:"Applications"},"hdr-apps"));for(const w of v){const A=x;f.push(n(Y,{isSelected:A===u,node:w},w.name)),x++}}if(h.length>0){f.push(n(X,{count:h.length,label:"Libraries"},"hdr-libs"));for(const w of h){const A=x;f.push(n(Y,{isSelected:A===u,node:w},w.name)),x++}}let C=0;v.length>0&&(C+=2+v.length),h.length>0&&(C+=2+h.length);const M=C>y&&y>0;return r(i,{borderColor:g,borderStyle:"single",flexDirection:"column",flexGrow:1,children:[r(i,{flexShrink:0,gap:1,paddingX:1,children:[n(t,{bold:!0,inverse:!0,children:" VIS "}),r(t,{wrap:"truncate",children:[a.total," ","packages"]}),r(t,{dimColor:!0,children:["(",a.apps," ","apps,"," ",a.libs," ","libs,"," ",a.deps," ","deps)"]})]}),n(i,{flexShrink:0,gap:1,paddingX:1,paddingY:1,children:Ne.map(w=>{const A=l===w.key;return r(i,{children:[n(t,{dimColor:!A,children:"["}),n(t,{bold:A,color:A?"cyan":"gray",children:w.shortcut}),n(t,{dimColor:!A,children:"]"}),r(t,{color:A?"white":"gray",children:[" ",w.label]})]},w.key)})}),o&&r(i,{flexShrink:0,paddingX:1,children:[n(t,{bold:!0,color:"white",children:"/ "}),n(t,{children:e}),n(t,{inverse:!0,children:" "})]}),r(i,{flexDirection:"row",flexGrow:1,overflow:"hidden",children:[n(i,{flexDirection:"column",flexGrow:1,overflow:"hidden",paddingLeft:1,children:n(i,{flexDirection:"column",marginTop:-s,children:f})}),M&&n(i,{flexShrink:0,marginLeft:1,marginRight:1,children:n(pe,{contentHeight:C,placement:"inset",scrollOffset:s,style:"block",viewportHeight:y})})]})]})},"ProjectListPanel");var Ie=Object.defineProperty,Pe=E((o,e)=>Ie(o,"name",{value:e,configurable:!0}),"M$1");const Le=100,Oe=40,Be=10,J={1:"all",2:"app",3:"lib"},Re=Pe(({autoExitSeconds:o=0,store:e})=>{const{exit:l}=he(),{columns:d,rows:c}=ue(),s=ve(e.subscribe,e.getSnapshot),[u,a]=O(!1),y=q(null),g=q(null),[v,h]=O(0),[f,x]=O(!1),C=_(()=>e.getFilteredNodes(),[s.allNodes,s.filterType,s.filterText]),M=_(()=>e.getStats(),[s.allNodes]),w=C[s.selectedIndex]??null,A=U(m=>{const p=C.filter(T=>T.type==="application"),b=C.filter(T=>T.type!=="application");let j=0,L=0;if(p.length>0){j+=2;for(let T=0;T<p.length;T++){if(L===m)return j;j+=1,L++}}if(b.length>0){j+=2;for(let T=0;T<b.length;T++){if(L===m)return j;j+=1,L++}}return j},[C]),P=Math.max(1,c-8-(s.filterActive?1:0)),N=U(m=>{const p=A(m);h(b=>p>b+P-2?Math.max(0,p-P+2):p<b+1?Math.max(0,p-1):b)},[A,P]);if(Ce(()=>{g.current?.scrollToTop()},[w?.name]),fe((m,p)=>{if(m==="c"&&p.ctrl){l();return}if(!f){if(u){p.escape||m==="?"?a(!1):m==="q"?(a(!1),x(!0)):p.downArrow||m==="j"?y.current?.scrollBy(1):(p.upArrow||m==="k")&&y.current?.scrollBy(-1);return}if(m==="?"){a(!0);return}if(m==="q"){x(!0);return}if(p.tab){e.setFocusedPanel(s.focusedPanel==="list"?"detail":"list");return}if(J[m]){e.setFilterType(J[m]);return}if(s.filterActive){if(p.escape){e.setFilterActive(!1);return}if(p.return){e.setFilterActive(!1);return}if(p.backspace){e.setFilter(s.filterText.slice(0,-1));return}if(m&&!p.ctrl&&!p.meta){e.setFilter(s.filterText+m);return}return}if(s.focusedPanel==="list"){if(C.length===0){m==="/"&&e.setFilterActive(!0);return}if(p.downArrow||m==="j"){const b=Math.min(s.selectedIndex+1,C.length-1);e.setSelectedIndex(b),N(b);return}if(p.upArrow||m==="k"){const b=Math.max(s.selectedIndex-1,0);e.setSelectedIndex(b),N(b);return}if(p.pageDown){const b=Math.min(s.selectedIndex+10,C.length-1);e.setSelectedIndex(b),N(b);return}if(p.pageUp){const b=Math.max(s.selectedIndex-10,0);e.setSelectedIndex(b),N(b);return}if(p.home){e.setSelectedIndex(0),h(0);return}if(p.end){const b=C.length-1;e.setSelectedIndex(b),N(b);return}if(m==="/"){e.setFilterActive(!0);return}if(p.rightArrow){e.setFocusedPanel("detail");return}return}if(s.focusedPanel==="detail"){if(p.escape||p.leftArrow){e.setFocusedPanel("list");return}if(p.downArrow||m==="j"){g.current?.scrollBy(1);return}if(p.upArrow||m==="k"){g.current?.scrollBy(-1);return}if(p.pageDown){g.current?.scrollBy(10);return}if(p.pageUp){g.current?.scrollBy(-10);return}if(p.home){g.current?.scrollToTop();return}p.end&&g.current?.scrollToBottom()}}},{isActive:!0}),d<Oe||c<Be)return n(i,{alignItems:"center",height:c,justifyContent:"center",width:d,children:r(t,{color:"yellow",children:["Terminal too small (",d,"x",c,")"]})});const ne=d>=Le,R=n(i,{borderBottom:!1,borderColor:"gray",borderLeft:!1,borderRight:!1,borderStyle:"single",flexShrink:0,children:r(i,{flexWrap:"wrap",gap:2,paddingX:1,children:[r(i,{gap:1,children:[n(t,{bold:!0,color:"white",children:"q"}),n(t,{dimColor:!0,children:"QUIT"})]},"q"),r(i,{gap:1,children:[n(t,{bold:!0,color:"white",children:"?"}),n(t,{dimColor:!0,children:"HELP"})]},"?"),r(i,{gap:1,children:[n(t,{bold:!0,color:"white",children:"↑↓"}),n(t,{dimColor:!0,children:"NAV"})]},"nav"),r(i,{gap:1,children:[n(t,{bold:!0,color:"white",children:"1-3 /"}),n(t,{dimColor:!0,children:"FILTER"})]},"f"),r(i,{gap:1,children:[n(t,{bold:!0,color:"white",children:"Tab"}),n(t,{dimColor:!0,children:"PANEL"})]},"t")]})}),F=r(ge,{footer:r(t,{dimColor:!0,children:[n(t,{bold:!0,color:"white",children:"↑↓"})," ","scroll"," ",n(t,{bold:!0,color:"white",children:"?"}),"/",n(t,{bold:!0,color:"white",children:"Esc"})," ","close"]}),scrollRef:y,title:"KEYBOARD SHORTCUTS",visible:u,width:52,children:[r(i,{flexDirection:"column",marginBottom:1,children:[r(i,{marginBottom:1,children:[n(t,{dimColor:!0,children:"── "}),n(t,{bold:!0,color:"white",children:"NAVIGATION"})]}),r(i,{children:[n(i,{width:24,children:r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","↑","/k"]}),n(t,{dimColor:!0,children:" Move up"})]})}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","↓","/j"]}),n(t,{dimColor:!0,children:" Move down"})]})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","Tab"]}),n(t,{dimColor:!0,children:" Switch panel"})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","→","/","←"]}),n(t,{dimColor:!0,children:" Focus detail/list"})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","PgUp/PgDn"]}),n(t,{dimColor:!0,children:" Jump 10 items"})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","Home/End"]}),n(t,{dimColor:!0,children:" Jump to start/end"})]})]}),r(i,{flexDirection:"column",marginBottom:1,children:[r(i,{marginBottom:1,children:[n(t,{dimColor:!0,children:"── "}),n(t,{bold:!0,color:"white",children:"FILTERS"})]}),r(i,{children:[n(i,{width:24,children:r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","1"]}),n(t,{dimColor:!0,children:" All"})]})}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","2"]}),n(t,{dimColor:!0,children:" Apps only"})]})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","3"]}),n(t,{dimColor:!0,children:" Libraries only"})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","/"]}),n(t,{dimColor:!0,children:" Text filter"})]})]}),r(i,{flexDirection:"column",children:[r(i,{marginBottom:1,children:[n(t,{dimColor:!0,children:"── "}),n(t,{bold:!0,color:"white",children:"ACTIONS"})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","q"]}),n(t,{dimColor:!0,children:" Quit"})]}),r(t,{children:[r(t,{bold:!0,color:"white",children:[" ","?"]}),n(t,{dimColor:!0,children:" Toggle help"})]})]})]}),H=n($e,{filterActive:s.filterActive,filterText:s.filterText,filterType:s.filterType,focused:s.focusedPanel==="list",nodes:C,scrollOffset:v,selectedIndex:s.selectedIndex,stats:M,viewportHeight:P}),G=n(je,{focused:s.focusedPanel==="detail",node:w,scrollRef:g});if(ne){const m=Math.floor(d*.35);return r(i,{flexDirection:"column",height:c,width:d,children:[r(i,{flexDirection:"row",flexGrow:1,children:[n(i,{flexGrow:1,children:H}),n(i,{width:m,children:G})]}),R,n(W,{autoExitSeconds:o??3,onCancel:E(()=>{x(!1)},"onCancel"),visible:f}),F]})}const re=Math.floor(c*.55);return r(i,{flexDirection:"column",height:c,width:d,children:[n(i,{height:re,children:H}),n(i,{flexGrow:1,children:G}),R,n(W,{autoExitSeconds:o??3,onCancel:E(()=>{x(!1)},"onCancel"),visible:f}),F]})},"VisGraphApp");var V=Object.freeze,ee=Object.defineProperty,D=E((o,e)=>ee(o,"name",{value:e,configurable:!0}),"x"),Fe=E((o,e)=>V(ee(o,"raw",{value:V(e||o.slice())})),"C");const te=D((o,e,l,d,c,s,u,a)=>{const y=l?k("└── "):k("├── "),g=c.has(o),v=g?k(" (*)"):"",h=d.get(o),f=h?.type==="application"?S(o):o;if(s.push(`${e}${y}${f}${v}`),g)return;c.add(o);const x=h?.deps??[],C=l?`${e} `:`${e}${k("│")} `;if(a>=u&&x.length>0){s.push(`${C}${k(`... ${x.length} more`)}`);return}for(let M=0;M<x.length;M++){const w=x[M];w&&te(w.target,C,M===x.length-1,d,c,s,u,a+1)}},"printDepsTree"),Q=D((o,e,l,d,c,s)=>{const u=e.get(o),a=u?.type==="application"?S(o):o;d.push(`${s}${a}`),l.add(o);const y=u?.deps??[];if(y.length===0){d.push(`${s} ${k("(no dependencies)")}`);return}if(c<=0){d.push(`${s} ${k(`... ${y.length} dependencies`)}`);return}for(let g=0;g<y.length;g++){const v=y[g];v&&te(v.target,s,g===y.length-1,e,l,d,c,1)}},"printRootProject"),K=D((o,e)=>{const l=new Map;for(const[h,f]of Object.entries(o.nodes))l.set(h,{deps:(o.dependencies[h]??[]).map(x=>({target:x.target,type:x.type})),name:h,type:f.type});const d=[],c=[];for(const[h,f]of l)f.type==="application"?d.push(h):c.push(h);d.sort(),c.sort();const s=d.length+c.length,u=Object.values(o.dependencies).reduce((h,f)=>h+f.length,0),a=[];if(a.push(S("Project Dependency Graph"),""),d.length>0){a.push(` ${S(z(`Applications (${d.length})`))}`,"");for(const h of d)Q(h,l,new Set,a,e," "),a.push("")}if(c.length>0){a.push(` ${S(z(`Libraries (${c.length})`))}`,"");for(const h of c)Q(h,l,new Set,a,e," "),a.push("")}const y=process.stdout.columns||80;a.push(k("─".repeat(Math.min(y,60)))),a.push(`${S(String(s))} packages ${k("·")} ${S(String(u))} dependencies ${k("·")} ${S(String(d.length))} apps${k(",")} ${S(String(c.length))} libraries`);const g=new Set;let v=!1;for(const h of[...d,...c]){const f=l.get(h)?.deps??[];for(const x of f)g.has(x.target)&&(v=!0),g.add(x.target);g.add(h)}return v&&a.push(k("(*) = already shown above")),a.join(`
2
- `)},"projectGraphToAscii"),He=D(o=>{const e=Object.values(o.nodes).map(l=>({name:l.name,type:l.type}));return{edges:Object.values(o.dependencies).flat(),nodes:e}},"projectGraphToJson");var Z;const Ge=D(o=>{const e=Object.values(o.nodes).map(u=>({name:u.name,type:u.type})),l=[];for(const u of Object.values(o.dependencies))for(const a of u)l.push({source:a.source,target:a.target,type:a.type});const d=e.filter(u=>u.type==="application"),c=e.filter(u=>u.type!=="application"),s={apps:d.length,edges:l,libs:c.length,nodes:e};return String.raw(Z||(Z=Fe([`<!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="UTF-8">
6
- <title>Project Dependency Graph</title>
7
- <style>
8
- * { margin: 0; padding: 0; box-sizing: border-box; }
9
- body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; overflow: hidden; }
10
- svg { width: 100vw; height: 100vh; }
11
- .edge { fill: none; marker-end: url(#arrow); }
12
- .node rect { rx: 8; ry: 8; cursor: pointer; transition: stroke-width 0.15s; }
13
- .node text { font-size: 12px; font-weight: 600; pointer-events: none; }
14
- .node:hover rect { stroke-width: 2.5; stroke: #fff; }
15
- #info { position: fixed; top: 16px; right: 16px; background: #1e293b; padding: 14px 20px; border-radius: 10px; font-size: 13px; border: 1px solid #334155; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
16
- #info b { font-variant-numeric: tabular-nums; }
17
- .app-count { color: #fbbf24; }
18
- .lib-count { color: #38bdf8; }
19
- .dep-count { color: #a78bfa; }
20
- #legend { position: fixed; bottom: 16px; left: 16px; background: #1e293b; padding: 12px 16px; border-radius: 10px; font-size: 12px; border: 1px solid #334155; display: flex; gap: 16px; align-items: center; }
21
- .legend-dot { width: 12px; height: 12px; border-radius: 3px; display: inline-block; vertical-align: middle; margin-right: 6px; }
22
- #tooltip { position: fixed; display: none; background: #1e293b; border: 1px solid #475569; border-radius: 8px; padding: 12px 16px; font-size: 12px; max-width: 320px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); z-index: 10; pointer-events: none; }
23
- #tooltip h3 { font-size: 14px; margin-bottom: 6px; }
24
- #tooltip .type-badge { display: inline-block; padding: 1px 8px; border-radius: 4px; font-size: 10px; font-weight: 700; text-transform: uppercase; margin-left: 8px; }
25
- #tooltip .dep-section { margin-top: 8px; color: #94a3b8; }
26
- #tooltip ul { list-style: none; padding-left: 0; margin-top: 4px; }
27
- #tooltip li { padding: 1px 0; color: #cbd5e1; }
28
- #tooltip li::before { content: "92 "; color: #64748b; }
29
- </style>
30
- </head>
31
- <body>
32
- <div id="info">
33
- <b class="app-count">`,`</b> apps &middot;
34
- <b class="lib-count">`,`</b> libraries &middot;
35
- <b class="dep-count">`,`</b> dependencies
36
- </div>
37
- <div id="legend">
38
- <span><span class="legend-dot" style="background:#fbbf24"></span>Application</span>
39
- <span><span class="legend-dot" style="background:#38bdf8"></span>Library</span>
40
- <span style="color:#64748b">&mdash; solid = static &nbsp; - - - = implicit</span>
41
- </div>
42
- <div id="tooltip"></div>
43
- <svg id="graph">
44
- <defs>
45
- <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">
46
- <path d="M 0 0 L 10 5 L 0 10 z" fill="#64748b"/>
47
- </marker>
48
- </defs>
49
- </svg>
50
- <script>
51
- const data = `,`;
52
- const svg = document.getElementById('graph');
53
- const tooltip = document.getElementById('tooltip');
54
- const W = window.innerWidth, H = window.innerHeight;
1
+ import { createRequire as __cjs_createRequire } from "node:module";
55
2
 
56
- // Build adjacency
57
- const depMap = {}, rdepMap = {};
58
- data.nodes.forEach(n => { depMap[n.name] = []; rdepMap[n.name] = []; });
59
- data.edges.forEach(e => { depMap[e.source]?.push(e.target); rdepMap[e.target]?.push(e.source); });
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
60
4
 
61
- // Escape text for safe DOM insertion
62
- function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.textContent; }
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
63
6
 
64
- // Force-directed layout
65
- const repulsion = 5000 + data.nodes.length * 150;
66
- const nodes = data.nodes.map(n => ({
67
- ...n, x: W/2 + (Math.random()-0.5)*Math.min(W*0.6, 600),
68
- y: H/2 + (Math.random()-0.5)*Math.min(H*0.6, 400), vx: 0, vy: 0
69
- }));
70
- const nodeMap = new Map(nodes.map(n => [n.name, n]));
71
- const edges = data.edges.map(e => ({
72
- source: nodeMap.get(e.source), target: nodeMap.get(e.target), type: e.type
73
- }));
74
-
75
- for (let iter = 0; iter < 400; iter++) {
76
- for (let i = 0; i < nodes.length; i++) {
77
- for (let j = i+1; j < nodes.length; j++) {
78
- let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;
79
- let d = Math.sqrt(dx*dx + dy*dy) || 1;
80
- let f = repulsion / (d * d);
81
- nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;
82
- nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;
83
- }
84
- }
85
- edges.forEach(e => {
86
- if (!e.source || !e.target) {
87
- return;
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
88
15
  }
89
- let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;
90
- let d = Math.sqrt(dx*dx + dy*dy) || 1;
91
- let f = (d - 180) * 0.008;
92
- e.source.vx += dx/d * f; e.source.vy += dy/d * f;
93
- e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;
94
- });
95
- nodes.forEach(n => {
96
- n.vx += (W/2 - n.x) * 0.001; n.vy += (H/2 - n.y) * 0.001;
97
- n.x += n.vx * 0.3; n.y += n.vy * 0.3;
98
- n.vx *= 0.75; n.vy *= 0.75;
99
- n.x = Math.max(80, Math.min(W-80, n.x));
100
- n.y = Math.max(40, Math.min(H-40, n.y));
101
- });
102
- }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
103
19
 
104
- // Measure text widths
105
- const measure = document.createElementNS('http://www.w3.org/2000/svg','text');
106
- measure.setAttribute('font-size','12'); measure.setAttribute('font-weight','600');
107
- measure.setAttribute('font-family','system-ui');
108
- svg.appendChild(measure);
109
- const widths = {};
110
- nodes.forEach(n => { measure.textContent = n.name; widths[n.name] = measure.getComputedTextLength(); });
111
- svg.removeChild(measure);
20
+ const {
21
+ open,
22
+ stat,
23
+ watch
24
+ } = __cjs_getBuiltinModule("node:fs/promises");
25
+ import { isAccessible } from '@visulima/fs';
26
+ import { p as pail, Y as loadVisTaskConfigsForWorkspace, d as discoverWorkspace, Z as loadEnvFile } from './bin.js';
27
+ import { join } from '@visulima/path';
28
+ import { R as REGISTRY_FILE_MODE, w as withServiceLock, r as readEntry, i as isAlive, d as deleteEntry, g as getRegistryDir, s as slugify, a as writeEntry, b as runReadiness, p as pruneDead, S as ServiceReadinessError, c as readAllEntries } from '../packem_shared/registry-CbqXI0rc.js';
29
+ const {
30
+ spawn
31
+ } = __cjs_getBuiltinModule("node:child_process");
32
+ import { formatAge } from './handler14.js';
112
33
 
113
- // Render edges
114
- edges.forEach(e => {
115
- if (!e.source || !e.target) {
116
- return;
34
+ const isWindows = process.platform === "win32";
35
+ const spawnDetached = async (input) => {
36
+ const { command, cwd, env, logFile } = input;
37
+ const logHandle = await open(logFile, "a", REGISTRY_FILE_MODE);
38
+ let child;
39
+ try {
40
+ const shell = isWindows ? "cmd" : "/bin/sh";
41
+ const args = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
42
+ child = spawn(shell, args, {
43
+ cwd,
44
+ detached: true,
45
+ env: { ...process.env, ...env },
46
+ stdio: ["ignore", logHandle.fd, logHandle.fd],
47
+ // Windows: spawn in a new console so the child isn't tied
48
+ // to this terminal's lifetime.
49
+ windowsHide: true
50
+ });
51
+ } finally {
52
+ await logHandle.close().catch(() => {
53
+ });
117
54
  }
118
- const sw = (widths[e.source.name]||80)/2 + 12;
119
- const tw = (widths[e.target.name]||80)/2 + 12;
120
- const dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;
121
- const d = Math.sqrt(dx*dx+dy*dy)||1;
122
- const x1 = e.source.x + dx/d*sw, y1 = e.source.y + dy/d*14;
123
- const x2 = e.target.x - dx/d*tw, y2 = e.target.y - dy/d*14;
124
- const line = document.createElementNS('http://www.w3.org/2000/svg','line');
125
- line.setAttribute('x1',x1); line.setAttribute('y1',y1);
126
- line.setAttribute('x2',x2); line.setAttribute('y2',y2);
127
- line.setAttribute('class','edge');
128
- const edgeColors = { implicit: '#475569', devDependency: '#888888', peerDependency: '#CC8800' };
129
- line.setAttribute('stroke', edgeColors[e.type] || '#64748b');
130
- line.setAttribute('stroke-width', '1.5');
131
- if (e.type === 'implicit' || e.type === 'peerDependency') {
132
- line.setAttribute('stroke-dasharray', '6,4');
133
- }
134
- if (e.type === 'devDependency') {
135
- line.setAttribute('stroke-dasharray', '3,3');
136
- }
137
- svg.appendChild(line);
138
- });
139
-
140
- // Render nodes
141
- nodes.forEach(n => {
142
- const w = (widths[n.name]||80) + 24;
143
- const h = 32;
144
- const isApp = n.type === 'application';
145
- const fill = isApp ? '#fbbf24' : '#38bdf8';
146
- const textFill = '#0f172a';
147
- const stroke = isApp ? '#f59e0b' : '#0284c7';
148
-
149
- const g = document.createElementNS('http://www.w3.org/2000/svg','g');
150
- g.setAttribute('class','node');
151
- g.setAttribute('transform','translate('+(n.x - w/2)+','+(n.y - h/2)+')');
152
- const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');
153
- rect.setAttribute('width', w); rect.setAttribute('height', h);
154
- rect.setAttribute('fill', fill); rect.setAttribute('stroke', stroke); rect.setAttribute('stroke-width','1.5');
155
- g.appendChild(rect);
156
- const text = document.createElementNS('http://www.w3.org/2000/svg','text');
157
- text.setAttribute('x', w/2); text.setAttribute('y', h/2 + 4.5);
158
- text.setAttribute('text-anchor','middle'); text.setAttribute('fill', textFill);
159
- text.textContent = n.name;
160
- g.appendChild(text);
161
-
162
- g.addEventListener('mouseenter', (ev) => {
163
- const deps = depMap[n.name] || [];
164
- const rdeps = rdepMap[n.name] || [];
165
-
166
- // Build tooltip using safe DOM methods
167
- tooltip.textContent = '';
168
-
169
- const heading = document.createElement('h3');
170
- heading.textContent = n.name;
171
- const badge = document.createElement('span');
172
- badge.className = 'type-badge';
173
- badge.style.background = fill;
174
- badge.style.color = '#0f172a';
175
- badge.textContent = n.type;
176
- heading.appendChild(badge);
177
- tooltip.appendChild(heading);
55
+ if (child.pid === void 0) {
56
+ await new Promise((resolve, reject) => {
57
+ child.once("spawn", () => resolve());
58
+ child.once("error", (error) => reject(error));
59
+ });
60
+ }
61
+ if (child.pid === void 0) {
62
+ throw new Error(`Failed to spawn detached process for command: ${command}`);
63
+ }
64
+ child.unref();
65
+ return { pid: child.pid };
66
+ };
178
67
 
179
- if (deps.length) {
180
- const label = document.createElement('div');
181
- label.className = 'dep-section';
182
- label.textContent = 'Depends on:';
183
- tooltip.appendChild(label);
184
- const ul = document.createElement('ul');
185
- deps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });
186
- tooltip.appendChild(ul);
68
+ const DEFAULT_KILL_GRACE_MS = 5e3;
69
+ const startService = async (input) => withServiceLock(input.workspaceRoot, input.id, async () => {
70
+ const existing = await readEntry(input.workspaceRoot, input.id);
71
+ if (existing && isAlive(existing.pid)) {
72
+ throw new Error(`Service ${input.id} is already running (pid ${String(existing.pid)})`);
73
+ }
74
+ if (existing) {
75
+ await deleteEntry(input.workspaceRoot, input.id, existing);
76
+ }
77
+ const registryDirectory = await getRegistryDir(input.workspaceRoot);
78
+ const slug = slugify(input.id);
79
+ const logFile = join(registryDirectory, `${slug}.log`);
80
+ const { pid } = await spawnDetached({
81
+ command: input.command,
82
+ cwd: input.cwd,
83
+ env: input.env,
84
+ logFile
85
+ });
86
+ const entry = {
87
+ command: input.command,
88
+ config: input.config,
89
+ cwd: input.cwd,
90
+ env: input.config.env ?? {},
91
+ id: input.id,
92
+ logFile,
93
+ pid,
94
+ slug,
95
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
96
+ visVersion: process.env["VIS_VERSION"] ?? "0.0.0"
97
+ };
98
+ await writeEntry(input.workspaceRoot, entry);
99
+ if (input.skipReadiness !== true) {
100
+ try {
101
+ await runReadiness(input.config, { timeoutMs: input.readinessTimeoutMs });
102
+ } catch (error) {
103
+ await stopServiceByPid(pid, input.config.killGracePeriodMs ?? DEFAULT_KILL_GRACE_MS).catch(() => {
104
+ });
105
+ await deleteEntry(input.workspaceRoot, input.id, entry).catch(() => {
106
+ });
107
+ throw error;
108
+ }
109
+ }
110
+ return { entry };
111
+ });
112
+ const stopService = async (input) => withServiceLock(input.workspaceRoot, input.id, async () => {
113
+ const entry = await readEntry(input.workspaceRoot, input.id);
114
+ if (!entry) {
115
+ return { stopped: false };
116
+ }
117
+ if (!isAlive(entry.pid)) {
118
+ await deleteEntry(input.workspaceRoot, input.id, entry);
119
+ return { stopped: false };
120
+ }
121
+ const graceMs = input.graceMs ?? entry.config.killGracePeriodMs ?? DEFAULT_KILL_GRACE_MS;
122
+ await stopServiceByPid(entry.pid, graceMs);
123
+ await deleteEntry(input.workspaceRoot, input.id, entry);
124
+ return { stopped: true };
125
+ });
126
+ const stopServiceByPid = async (pid, graceMs) => {
127
+ sendSignalToServiceGroup(pid, "SIGTERM");
128
+ const start = Date.now();
129
+ while (Date.now() - start < graceMs) {
130
+ if (!isAlive(pid)) {
131
+ return;
187
132
  }
188
- if (rdeps.length) {
189
- const label = document.createElement('div');
190
- label.className = 'dep-section';
191
- label.textContent = 'Required by:';
192
- tooltip.appendChild(label);
193
- const ul = document.createElement('ul');
194
- rdeps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });
195
- tooltip.appendChild(ul);
133
+ await new Promise((resolve) => {
134
+ setTimeout(resolve, 100);
135
+ });
136
+ }
137
+ if (isAlive(pid)) {
138
+ sendSignalToServiceGroup(pid, "SIGKILL");
139
+ }
140
+ };
141
+ const sendSignalToServiceGroup = (pid, signal) => {
142
+ try {
143
+ if (process.platform === "win32") {
144
+ process.kill(pid, signal);
145
+ } else {
146
+ process.kill(-pid, signal);
196
147
  }
197
- if (!deps.length && !rdeps.length) {
198
- const empty = document.createElement('div');
199
- empty.style.marginTop = '6px';
200
- empty.style.color = '#64748b';
201
- empty.textContent = 'No dependencies';
202
- tooltip.appendChild(empty);
148
+ } catch (error) {
149
+ if (error.code === "ESRCH") {
150
+ return;
203
151
  }
152
+ throw error;
153
+ }
154
+ };
204
155
 
205
- tooltip.style.display = 'block';
206
- const rect = tooltip.getBoundingClientRect();
207
- tooltip.style.left = Math.min(ev.clientX + 12, W - rect.width - 12) + 'px';
208
- tooltip.style.top = Math.min(ev.clientY + 12, H - rect.height - 12) + 'px';
209
- });
210
- g.addEventListener('mouseleave', () => { tooltip.style.display = 'none'; });
211
- svg.appendChild(g);
212
- });
213
- <\/script>
214
- </body>
215
- </html>`],[`<!DOCTYPE html>
216
- <html lang="en">
217
- <head>
218
- <meta charset="UTF-8">
219
- <title>Project Dependency Graph</title>
220
- <style>
221
- * { margin: 0; padding: 0; box-sizing: border-box; }
222
- body { font-family: system-ui, -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; overflow: hidden; }
223
- svg { width: 100vw; height: 100vh; }
224
- .edge { fill: none; marker-end: url(#arrow); }
225
- .node rect { rx: 8; ry: 8; cursor: pointer; transition: stroke-width 0.15s; }
226
- .node text { font-size: 12px; font-weight: 600; pointer-events: none; }
227
- .node:hover rect { stroke-width: 2.5; stroke: #fff; }
228
- #info { position: fixed; top: 16px; right: 16px; background: #1e293b; padding: 14px 20px; border-radius: 10px; font-size: 13px; border: 1px solid #334155; box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
229
- #info b { font-variant-numeric: tabular-nums; }
230
- .app-count { color: #fbbf24; }
231
- .lib-count { color: #38bdf8; }
232
- .dep-count { color: #a78bfa; }
233
- #legend { position: fixed; bottom: 16px; left: 16px; background: #1e293b; padding: 12px 16px; border-radius: 10px; font-size: 12px; border: 1px solid #334155; display: flex; gap: 16px; align-items: center; }
234
- .legend-dot { width: 12px; height: 12px; border-radius: 3px; display: inline-block; vertical-align: middle; margin-right: 6px; }
235
- #tooltip { position: fixed; display: none; background: #1e293b; border: 1px solid #475569; border-radius: 8px; padding: 12px 16px; font-size: 12px; max-width: 320px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); z-index: 10; pointer-events: none; }
236
- #tooltip h3 { font-size: 14px; margin-bottom: 6px; }
237
- #tooltip .type-badge { display: inline-block; padding: 1px 8px; border-radius: 4px; font-size: 10px; font-weight: 700; text-transform: uppercase; margin-left: 8px; }
238
- #tooltip .dep-section { margin-top: 8px; color: #94a3b8; }
239
- #tooltip ul { list-style: none; padding-left: 0; margin-top: 4px; }
240
- #tooltip li { padding: 1px 0; color: #cbd5e1; }
241
- #tooltip li::before { content: "\\2192 "; color: #64748b; }
242
- </style>
243
- </head>
244
- <body>
245
- <div id="info">
246
- <b class="app-count">`,`</b> apps &middot;
247
- <b class="lib-count">`,`</b> libraries &middot;
248
- <b class="dep-count">`,`</b> dependencies
249
- </div>
250
- <div id="legend">
251
- <span><span class="legend-dot" style="background:#fbbf24"></span>Application</span>
252
- <span><span class="legend-dot" style="background:#38bdf8"></span>Library</span>
253
- <span style="color:#64748b">&mdash; solid = static &nbsp; - - - = implicit</span>
254
- </div>
255
- <div id="tooltip"></div>
256
- <svg id="graph">
257
- <defs>
258
- <marker id="arrow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="6" markerHeight="6" orient="auto">
259
- <path d="M 0 0 L 10 5 L 0 10 z" fill="#64748b"/>
260
- </marker>
261
- </defs>
262
- </svg>
263
- <script>
264
- const data = `,`;
265
- const svg = document.getElementById('graph');
266
- const tooltip = document.getElementById('tooltip');
267
- const W = window.innerWidth, H = window.innerHeight;
268
-
269
- // Build adjacency
270
- const depMap = {}, rdepMap = {};
271
- data.nodes.forEach(n => { depMap[n.name] = []; rdepMap[n.name] = []; });
272
- data.edges.forEach(e => { depMap[e.source]?.push(e.target); rdepMap[e.target]?.push(e.source); });
273
-
274
- // Escape text for safe DOM insertion
275
- function esc(s) { const d = document.createElement('div'); d.textContent = s; return d.textContent; }
276
-
277
- // Force-directed layout
278
- const repulsion = 5000 + data.nodes.length * 150;
279
- const nodes = data.nodes.map(n => ({
280
- ...n, x: W/2 + (Math.random()-0.5)*Math.min(W*0.6, 600),
281
- y: H/2 + (Math.random()-0.5)*Math.min(H*0.6, 400), vx: 0, vy: 0
282
- }));
283
- const nodeMap = new Map(nodes.map(n => [n.name, n]));
284
- const edges = data.edges.map(e => ({
285
- source: nodeMap.get(e.source), target: nodeMap.get(e.target), type: e.type
286
- }));
287
-
288
- for (let iter = 0; iter < 400; iter++) {
289
- for (let i = 0; i < nodes.length; i++) {
290
- for (let j = i+1; j < nodes.length; j++) {
291
- let dx = nodes[j].x - nodes[i].x, dy = nodes[j].y - nodes[i].y;
292
- let d = Math.sqrt(dx*dx + dy*dy) || 1;
293
- let f = repulsion / (d * d);
294
- nodes[i].vx -= dx/d * f; nodes[i].vy -= dy/d * f;
295
- nodes[j].vx += dx/d * f; nodes[j].vy += dy/d * f;
156
+ const splitTargetId = (raw) => {
157
+ const id = raw.trim();
158
+ const idx = id.lastIndexOf(":");
159
+ if (idx <= 0 || idx === id.length - 1) {
160
+ return void 0;
161
+ }
162
+ return { project: id.slice(0, idx), target: id.slice(idx + 1) };
163
+ };
164
+ const resolveCwd = (workspaceRoot, projectRoot, runFromWorkspaceRoot) => {
165
+ if (runFromWorkspaceRoot || !projectRoot) {
166
+ return workspaceRoot;
167
+ }
168
+ return projectRoot.startsWith("/") ? projectRoot : `${workspaceRoot}/${projectRoot}`;
169
+ };
170
+ const resolveTarget = async (workspaceRoot, visConfig, targetId) => {
171
+ const split = splitTargetId(targetId);
172
+ if (!split) {
173
+ pail.error(`Invalid target id "${targetId}". Expected "<project>:<target>", e.g. "@my/api:db".`);
174
+ return void 0;
175
+ }
176
+ const taskConfigs = await loadVisTaskConfigsForWorkspace(workspaceRoot);
177
+ const { projectOptions, workspace } = discoverWorkspace(workspaceRoot, visConfig, taskConfigs);
178
+ const project = workspace.projects[split.project];
179
+ const target = projectOptions.get(split.project)?.[split.target];
180
+ if (!project || !target) {
181
+ pail.error(`Target "${targetId}" not found in this workspace.`);
182
+ return void 0;
183
+ }
184
+ const service = target.options?.service;
185
+ if (!service) {
186
+ pail.error(`Target "${targetId}" is not a service. Add an \`options.service\` block to make it eligible for \`vis service\`.`);
187
+ return void 0;
188
+ }
189
+ if (!target.command) {
190
+ pail.error(`Target "${targetId}" has no command services must be runnable.`);
191
+ return void 0;
192
+ }
193
+ const cwd = resolveCwd(workspaceRoot, project.root, target.options?.runFromWorkspaceRoot === true);
194
+ const envFromFiles = target.options?.envFile ? loadEnvFile(cwd, target.options.envFile) : {};
195
+ return {
196
+ command: target.command,
197
+ cwd,
198
+ env: { ...envFromFiles, ...service.env },
199
+ service,
200
+ target,
201
+ targetId
202
+ };
203
+ };
204
+ const requireWorkspace = (wsRoot) => {
205
+ if (!wsRoot) {
206
+ throw new Error("Could not determine workspace root. Run `vis service` inside a workspace.");
207
+ }
208
+ return wsRoot;
209
+ };
210
+ const serviceStartExecute = async ({ argument, options, visConfig, workspaceRoot: wsRoot }) => {
211
+ const workspaceRoot = requireWorkspace(wsRoot);
212
+ const targetId = argument[0]?.trim();
213
+ if (!targetId) {
214
+ pail.error("Missing target id. Usage: vis service start <project>:<target>");
215
+ process.exitCode = 1;
216
+ return;
217
+ }
218
+ const resolved = await resolveTarget(workspaceRoot, visConfig, targetId);
219
+ if (!resolved) {
220
+ process.exitCode = 1;
221
+ return;
222
+ }
223
+ try {
224
+ const { entry } = await startService({
225
+ command: resolved.command,
226
+ config: resolved.service,
227
+ cwd: resolved.cwd,
228
+ env: resolved.env,
229
+ id: targetId,
230
+ readinessTimeoutMs: options.timeout,
231
+ skipReadiness: options.noReadiness === true,
232
+ workspaceRoot
233
+ });
234
+ pail.success(`Started ${targetId} (pid ${String(entry.pid)})`);
235
+ pail.info(` log: ${entry.logFile}`);
236
+ } catch (error) {
237
+ const message = error instanceof Error ? error.message : String(error);
238
+ if (error instanceof ServiceReadinessError) {
239
+ pail.error(`Readiness probe failed for ${targetId}: ${message}`);
240
+ } else {
241
+ pail.error(`Failed to start ${targetId}: ${message}`);
296
242
  }
243
+ process.exitCode = 1;
297
244
  }
298
- edges.forEach(e => {
299
- if (!e.source || !e.target) {
300
- return;
245
+ };
246
+ const stopOne = async (workspaceRoot, id, graceMs) => {
247
+ const { stopped } = await stopService({ graceMs, id, workspaceRoot });
248
+ if (stopped) {
249
+ pail.success(`Stopped ${id}`);
250
+ return true;
251
+ }
252
+ pail.info(`No running service registered for ${id}`);
253
+ return false;
254
+ };
255
+ const serviceStopExecute = async ({ argument, options, workspaceRoot: wsRoot }) => {
256
+ const workspaceRoot = requireWorkspace(wsRoot);
257
+ const { graceMs } = options;
258
+ const targetId = argument[0]?.trim();
259
+ if (options.all === true) {
260
+ if (targetId) {
261
+ pail.error("Cannot combine --all with a target id. Use one or the other.");
262
+ process.exitCode = 1;
263
+ return;
301
264
  }
302
- let dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;
303
- let d = Math.sqrt(dx*dx + dy*dy) || 1;
304
- let f = (d - 180) * 0.008;
305
- e.source.vx += dx/d * f; e.source.vy += dy/d * f;
306
- e.target.vx -= dx/d * f; e.target.vy -= dy/d * f;
307
- });
308
- nodes.forEach(n => {
309
- n.vx += (W/2 - n.x) * 0.001; n.vy += (H/2 - n.y) * 0.001;
310
- n.x += n.vx * 0.3; n.y += n.vy * 0.3;
311
- n.vx *= 0.75; n.vy *= 0.75;
312
- n.x = Math.max(80, Math.min(W-80, n.x));
313
- n.y = Math.max(40, Math.min(H-40, n.y));
314
- });
315
- }
316
-
317
- // Measure text widths
318
- const measure = document.createElementNS('http://www.w3.org/2000/svg','text');
319
- measure.setAttribute('font-size','12'); measure.setAttribute('font-weight','600');
320
- measure.setAttribute('font-family','system-ui');
321
- svg.appendChild(measure);
322
- const widths = {};
323
- nodes.forEach(n => { measure.textContent = n.name; widths[n.name] = measure.getComputedTextLength(); });
324
- svg.removeChild(measure);
325
-
326
- // Render edges
327
- edges.forEach(e => {
328
- if (!e.source || !e.target) {
265
+ const entries = await readAllEntries(workspaceRoot);
266
+ if (entries.length === 0) {
267
+ pail.info("No running services registered for this workspace.");
329
268
  return;
269
+ }
270
+ for (const entry of entries) {
271
+ await stopOne(workspaceRoot, entry.id, graceMs);
272
+ }
273
+ return;
330
274
  }
331
- const sw = (widths[e.source.name]||80)/2 + 12;
332
- const tw = (widths[e.target.name]||80)/2 + 12;
333
- const dx = e.target.x - e.source.x, dy = e.target.y - e.source.y;
334
- const d = Math.sqrt(dx*dx+dy*dy)||1;
335
- const x1 = e.source.x + dx/d*sw, y1 = e.source.y + dy/d*14;
336
- const x2 = e.target.x - dx/d*tw, y2 = e.target.y - dy/d*14;
337
- const line = document.createElementNS('http://www.w3.org/2000/svg','line');
338
- line.setAttribute('x1',x1); line.setAttribute('y1',y1);
339
- line.setAttribute('x2',x2); line.setAttribute('y2',y2);
340
- line.setAttribute('class','edge');
341
- const edgeColors = { implicit: '#475569', devDependency: '#888888', peerDependency: '#CC8800' };
342
- line.setAttribute('stroke', edgeColors[e.type] || '#64748b');
343
- line.setAttribute('stroke-width', '1.5');
344
- if (e.type === 'implicit' || e.type === 'peerDependency') {
345
- line.setAttribute('stroke-dasharray', '6,4');
346
- }
347
- if (e.type === 'devDependency') {
348
- line.setAttribute('stroke-dasharray', '3,3');
349
- }
350
- svg.appendChild(line);
351
- });
352
-
353
- // Render nodes
354
- nodes.forEach(n => {
355
- const w = (widths[n.name]||80) + 24;
356
- const h = 32;
357
- const isApp = n.type === 'application';
358
- const fill = isApp ? '#fbbf24' : '#38bdf8';
359
- const textFill = '#0f172a';
360
- const stroke = isApp ? '#f59e0b' : '#0284c7';
361
-
362
- const g = document.createElementNS('http://www.w3.org/2000/svg','g');
363
- g.setAttribute('class','node');
364
- g.setAttribute('transform','translate('+(n.x - w/2)+','+(n.y - h/2)+')');
365
- const rect = document.createElementNS('http://www.w3.org/2000/svg','rect');
366
- rect.setAttribute('width', w); rect.setAttribute('height', h);
367
- rect.setAttribute('fill', fill); rect.setAttribute('stroke', stroke); rect.setAttribute('stroke-width','1.5');
368
- g.appendChild(rect);
369
- const text = document.createElementNS('http://www.w3.org/2000/svg','text');
370
- text.setAttribute('x', w/2); text.setAttribute('y', h/2 + 4.5);
371
- text.setAttribute('text-anchor','middle'); text.setAttribute('fill', textFill);
372
- text.textContent = n.name;
373
- g.appendChild(text);
374
-
375
- g.addEventListener('mouseenter', (ev) => {
376
- const deps = depMap[n.name] || [];
377
- const rdeps = rdepMap[n.name] || [];
378
-
379
- // Build tooltip using safe DOM methods
380
- tooltip.textContent = '';
381
-
382
- const heading = document.createElement('h3');
383
- heading.textContent = n.name;
384
- const badge = document.createElement('span');
385
- badge.className = 'type-badge';
386
- badge.style.background = fill;
387
- badge.style.color = '#0f172a';
388
- badge.textContent = n.type;
389
- heading.appendChild(badge);
390
- tooltip.appendChild(heading);
391
-
392
- if (deps.length) {
393
- const label = document.createElement('div');
394
- label.className = 'dep-section';
395
- label.textContent = 'Depends on:';
396
- tooltip.appendChild(label);
397
- const ul = document.createElement('ul');
398
- deps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });
399
- tooltip.appendChild(ul);
275
+ if (!targetId) {
276
+ pail.error("Missing target id. Usage: vis service stop <project>:<target> | --all");
277
+ process.exitCode = 1;
278
+ return;
279
+ }
280
+ const stopped = await stopOne(workspaceRoot, targetId, graceMs);
281
+ if (!stopped) {
282
+ process.exitCode = 1;
283
+ }
284
+ };
285
+ const formatPort = (entry) => {
286
+ const port = entry.config.readiness?.tcp.port ?? entry.config.port;
287
+ return port === void 0 ? "—" : String(port);
288
+ };
289
+ const VALID_LIST_FORMATS = /* @__PURE__ */ new Set(["json", "table"]);
290
+ const serviceListExecute = async ({ logger, options, workspaceRoot: wsRoot }) => {
291
+ const workspaceRoot = requireWorkspace(wsRoot);
292
+ const format = options.format ?? "table";
293
+ if (!VALID_LIST_FORMATS.has(format)) {
294
+ pail.error(`Invalid --format "${format}". Expected one of: ${[...VALID_LIST_FORMATS].sort().join(", ")}.`);
295
+ process.exitCode = 1;
296
+ return;
297
+ }
298
+ const { surviving: entries } = await pruneDead(workspaceRoot);
299
+ if (format === "json") {
300
+ const now = Date.now();
301
+ process.stdout.write(
302
+ `${JSON.stringify(
303
+ entries.map((entry) => {
304
+ const startedMs = Date.parse(entry.startedAt);
305
+ return {
306
+ ageMs: Number.isFinite(startedMs) ? now - startedMs : null,
307
+ alive: isAlive(entry.pid),
308
+ command: entry.command,
309
+ cwd: entry.cwd,
310
+ env: entry.env,
311
+ id: entry.id,
312
+ logFile: entry.logFile,
313
+ pid: entry.pid,
314
+ port: entry.config.readiness?.tcp.port ?? entry.config.port ?? null,
315
+ startedAt: entry.startedAt,
316
+ visVersion: entry.visVersion
317
+ };
318
+ }),
319
+ void 0,
320
+ 2
321
+ )}
322
+ `
323
+ );
324
+ return;
325
+ }
326
+ if (entries.length === 0) {
327
+ pail.info("No running services registered for this workspace.");
328
+ return;
329
+ }
330
+ const renderedAt = Date.now();
331
+ const rows = entries.map((entry) => {
332
+ const startedMs = Date.parse(entry.startedAt);
333
+ return {
334
+ age: Number.isFinite(startedMs) ? formatAge(startedMs, renderedAt) : "?",
335
+ id: entry.id,
336
+ log: entry.logFile,
337
+ pid: String(entry.pid),
338
+ port: formatPort(entry)
339
+ };
340
+ });
341
+ const idWidth = Math.max(2, ...rows.map((r) => r.id.length));
342
+ const pidWidth = Math.max(3, ...rows.map((r) => r.pid.length));
343
+ const portWidth = Math.max(4, ...rows.map((r) => r.port.length));
344
+ const ageWidth = Math.max(3, ...rows.map((r) => r.age.length));
345
+ logger.info(` ${"id".padEnd(idWidth)} ${"pid".padEnd(pidWidth)} ${"port".padEnd(portWidth)} ${"age".padEnd(ageWidth)} log`);
346
+ logger.info(` ${"-".repeat(idWidth)} ${"-".repeat(pidWidth)} ${"-".repeat(portWidth)} ${"-".repeat(ageWidth)} ---`);
347
+ for (const row of rows) {
348
+ logger.info(` ${row.id.padEnd(idWidth)} ${row.pid.padEnd(pidWidth)} ${row.port.padEnd(portWidth)} ${row.age.padEnd(ageWidth)} ${row.log}`);
349
+ }
350
+ };
351
+ const serviceStatusExecute = async ({ argument, options, workspaceRoot: wsRoot }) => {
352
+ const workspaceRoot = requireWorkspace(wsRoot);
353
+ const targetId = argument[0]?.trim();
354
+ if (!targetId) {
355
+ pail.error("Missing target id. Usage: vis service status <project>:<target>");
356
+ process.exitCode = 1;
357
+ return;
358
+ }
359
+ const entry = await readEntry(workspaceRoot, targetId);
360
+ if (!entry) {
361
+ pail.error(`No service registered for ${targetId}.`);
362
+ process.exitCode = 1;
363
+ return;
364
+ }
365
+ if (!isAlive(entry.pid)) {
366
+ pail.error(`Service ${targetId} is not running (pid ${String(entry.pid)} is dead). Run \`vis service start ${targetId}\` to recover.`);
367
+ process.exitCode = 1;
368
+ return;
369
+ }
370
+ try {
371
+ await runReadiness(entry.config, { timeoutMs: options.timeout });
372
+ pail.success(`${targetId} healthy (pid ${String(entry.pid)})`);
373
+ } catch (error) {
374
+ const message = error instanceof Error ? error.message : String(error);
375
+ pail.error(`${targetId} probe failed: ${message}`);
376
+ process.exitCode = 1;
377
+ }
378
+ };
379
+ const serviceRestartExecute = async ({
380
+ argument,
381
+ options,
382
+ visConfig,
383
+ workspaceRoot: wsRoot
384
+ }) => {
385
+ const workspaceRoot = requireWorkspace(wsRoot);
386
+ const targetId = argument[0]?.trim();
387
+ if (!targetId) {
388
+ pail.error("Missing target id. Usage: vis service restart <project>:<target>");
389
+ process.exitCode = 1;
390
+ return;
391
+ }
392
+ await stopService({ graceMs: options.graceMs, id: targetId, workspaceRoot });
393
+ const resolved = await resolveTarget(workspaceRoot, visConfig, targetId);
394
+ if (!resolved) {
395
+ process.exitCode = 1;
396
+ return;
397
+ }
398
+ try {
399
+ const { entry } = await startService({
400
+ command: resolved.command,
401
+ config: resolved.service,
402
+ cwd: resolved.cwd,
403
+ env: resolved.env,
404
+ id: targetId,
405
+ readinessTimeoutMs: options.timeout,
406
+ skipReadiness: options.noReadiness === true,
407
+ workspaceRoot
408
+ });
409
+ pail.success(`Restarted ${targetId} (pid ${String(entry.pid)})`);
410
+ } catch (error) {
411
+ const message = error instanceof Error ? error.message : String(error);
412
+ pail.error(`Failed to restart ${targetId}: ${message}`);
413
+ process.exitCode = 1;
414
+ }
415
+ };
416
+ const TAIL_POLL_MS = 200;
417
+ const TAIL_MAX_CHUNK_BYTES = 1024 * 1024;
418
+ const tailLog = async (logFile) => {
419
+ let position = 0;
420
+ try {
421
+ const initial = await stat(logFile);
422
+ position = initial.size;
423
+ } catch {
424
+ position = 0;
425
+ }
426
+ const controller = new AbortController();
427
+ let sigintReceived = false;
428
+ const onSigint = () => {
429
+ sigintReceived = true;
430
+ controller.abort();
431
+ };
432
+ const onSigterm = () => {
433
+ controller.abort();
434
+ };
435
+ process.on("SIGINT", onSigint);
436
+ process.on("SIGTERM", onSigterm);
437
+ let tickInFlight = false;
438
+ const tickOnce = async () => {
439
+ if (tickInFlight) {
440
+ return;
400
441
  }
401
- if (rdeps.length) {
402
- const label = document.createElement('div');
403
- label.className = 'dep-section';
404
- label.textContent = 'Required by:';
405
- tooltip.appendChild(label);
406
- const ul = document.createElement('ul');
407
- rdeps.forEach(d => { const li = document.createElement('li'); li.textContent = d; ul.appendChild(li); });
408
- tooltip.appendChild(ul);
442
+ tickInFlight = true;
443
+ try {
444
+ const stats = await stat(logFile).catch(() => void 0);
445
+ if (!stats) {
446
+ return;
447
+ }
448
+ if (stats.size < position) {
449
+ position = 0;
450
+ }
451
+ if (stats.size === position) {
452
+ return;
453
+ }
454
+ const available = stats.size - position;
455
+ const chunkSize = Math.min(available, TAIL_MAX_CHUNK_BYTES);
456
+ const handle = await open(logFile, "r");
457
+ try {
458
+ const buffer = Buffer.alloc(chunkSize);
459
+ await handle.read(buffer, 0, chunkSize, position);
460
+ process.stdout.write(buffer);
461
+ position += chunkSize;
462
+ } finally {
463
+ await handle.close().catch(() => {
464
+ });
465
+ }
466
+ } finally {
467
+ tickInFlight = false;
409
468
  }
410
- if (!deps.length && !rdeps.length) {
411
- const empty = document.createElement('div');
412
- empty.style.marginTop = '6px';
413
- empty.style.color = '#64748b';
414
- empty.textContent = 'No dependencies';
415
- tooltip.appendChild(empty);
469
+ };
470
+ try {
471
+ const watcher = (async () => {
472
+ try {
473
+ for await (const _event of watch(logFile, { signal: controller.signal })) {
474
+ await tickOnce();
475
+ }
476
+ } catch {
477
+ }
478
+ })();
479
+ while (!controller.signal.aborted) {
480
+ await tickOnce();
481
+ await new Promise((resolve) => {
482
+ const timer = setTimeout(resolve, TAIL_POLL_MS);
483
+ controller.signal.addEventListener(
484
+ "abort",
485
+ () => {
486
+ clearTimeout(timer);
487
+ resolve();
488
+ },
489
+ { once: true }
490
+ );
491
+ });
416
492
  }
493
+ await watcher.catch(() => {
494
+ });
495
+ await tickOnce().catch(() => {
496
+ });
497
+ } finally {
498
+ process.off("SIGINT", onSigint);
499
+ process.off("SIGTERM", onSigterm);
500
+ }
501
+ if (sigintReceived) {
502
+ process.exitCode = 130;
503
+ }
504
+ };
505
+ const serviceLogsExecute = async ({ argument, options, workspaceRoot: wsRoot }) => {
506
+ const workspaceRoot = requireWorkspace(wsRoot);
507
+ const targetId = argument[0]?.trim();
508
+ if (!targetId) {
509
+ pail.error("Missing target id. Usage: vis service logs <project>:<target>");
510
+ process.exitCode = 1;
511
+ return;
512
+ }
513
+ const entry = await readEntry(workspaceRoot, targetId);
514
+ if (!entry) {
515
+ pail.error(`No service registered for ${targetId}.`);
516
+ process.exitCode = 1;
517
+ return;
518
+ }
519
+ if (!await isAccessible(entry.logFile)) {
520
+ pail.warn(`Log file is missing for ${targetId}: ${entry.logFile}`);
521
+ process.exitCode = 1;
522
+ return;
523
+ }
524
+ if (options.follow === true) {
525
+ await tailLog(entry.logFile);
526
+ return;
527
+ }
528
+ const handle = await open(entry.logFile, "r");
529
+ try {
530
+ const stream = handle.createReadStream();
531
+ await new Promise((resolve, reject) => {
532
+ stream.on("end", resolve);
533
+ stream.on("error", reject);
534
+ stream.pipe(process.stdout, { end: false });
535
+ });
536
+ } finally {
537
+ await handle.close().catch(() => {
538
+ });
539
+ }
540
+ };
417
541
 
418
- tooltip.style.display = 'block';
419
- const rect = tooltip.getBoundingClientRect();
420
- tooltip.style.left = Math.min(ev.clientX + 12, W - rect.width - 12) + 'px';
421
- tooltip.style.top = Math.min(ev.clientY + 12, H - rect.height - 12) + 'px';
422
- });
423
- g.addEventListener('mouseleave', () => { tooltip.style.display = 'none'; });
424
- svg.appendChild(g);
425
- });
426
- <\/script>
427
- </body>
428
- </html>`])),s.apps,s.libs,s.edges.length,JSON.stringify(s).replaceAll("</",String.raw`<\/`))},"projectGraphToHtml"),Ve=D(async({logger:o,options:e,visConfig:l,workspaceRoot:d})=>{if(!d)throw new Error("Could not determine workspace root. Run this command inside a monorepo.");const c=d,{packageJsons:s,workspace:u}=xe(c,l),a=be(c,u,s),y=!!process.stdout.isTTY&&!ye,g=e.format??(y?"tui":"ascii"),v=e.output,h=e.depth??1/0;let f;switch(g){case"dot":{f=ce(a);break}case"html":{f=Ge(a);break}case"json":{f=JSON.stringify(He(a),void 0,2);break}case"tui":{if(!y){f=K(a,h);break}const x=l?.tui?.autoExit===!0?3:typeof l?.tui?.autoExit=="number"?l.tui.autoExit:0;process.stdin.isTTY&&typeof process.stdin.setRawMode=="function"&&(process.stdin.setRawMode(!0),process.stdin.ref(),process.stdin.resume());const C=setInterval(()=>{},1e3),M=new Me(a);await me(we.createElement(Re,{autoExitSeconds:x,store:M}),{alternateScreen:!0,exitOnCtrlC:!1,interactive:!0,patchConsole:!0}).waitUntilExit(),clearInterval(C);return}default:f=K(a,h)}v?(se(v,f,"utf8"),o.info(`Graph written to ${v}`)):o.info(f)},"execute");export{Ve as default};
542
+ export { serviceListExecute, serviceLogsExecute, serviceRestartExecute, serviceStartExecute, serviceStatusExecute, serviceStopExecute };