cloudwire 0.1.0__py3-none-any.whl
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.
- cloudwire/__init__.py +3 -0
- cloudwire/app/__init__.py +1 -0
- cloudwire/app/graph_store.py +88 -0
- cloudwire/app/main.py +453 -0
- cloudwire/app/models.py +83 -0
- cloudwire/app/scan_jobs.py +368 -0
- cloudwire/app/scanner.py +1149 -0
- cloudwire/cli.py +86 -0
- cloudwire/static/assets/index-CByMF4j6.js +40 -0
- cloudwire/static/assets/index-lik1Sxh5.css +1 -0
- cloudwire/static/index.html +13 -0
- cloudwire-0.1.0.dist-info/METADATA +186 -0
- cloudwire-0.1.0.dist-info/RECORD +16 -0
- cloudwire-0.1.0.dist-info/WHEEL +5 -0
- cloudwire-0.1.0.dist-info/entry_points.txt +2 -0
- cloudwire-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;700&display=swap";.cloudwire-page{height:100vh;display:grid;grid-template-rows:auto minmax(0,1fr);gap:10px;padding:10px;overflow:hidden;background:radial-gradient(circle at top,rgba(255,153,0,.08),transparent 26%),radial-gradient(circle at 80% 18%,rgba(0,231,255,.08),transparent 18%),#05080d;color:#d6e3ef}.topbar-shell,.sidebar-shell,.inspector-shell,.graph-stage-shell{background:#070d14;border:1px solid #0f1e2a;box-shadow:none}.topbar-shell{border-radius:0;padding:0 24px;height:52px;overflow:visible;display:flex;align-items:center;justify-content:space-between;position:relative;z-index:100}.topbar-left,.topbar-right{display:flex;align-items:center;gap:12px}.topbar-right{min-width:0}.topbar-mark{display:flex;align-items:center;gap:8px}.topbar-mark-dot{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px}.topbar-brand{font-size:15px;font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:#e8c888}.topbar-divider{width:1px;height:20px;background:#1a2a38}.topbar-kicker,.sidebar-section-title,.inspector-kicker{color:#2a4a5a;font-size:11px;letter-spacing:.2em;text-transform:uppercase}.topbar-compact-select,.topbar-services-inline{height:32px;border:1px solid #1a2a38;background:#0c1820;color:#c8d8e8;padding:0 10px;font:inherit;font-size:12px}.topbar-region-select{color:#3a5a6a}.topbar-services-inline{width:260px}.topbar-scan-inline{display:flex;align-items:center;gap:8px;color:#f90;font-size:12px}.topbar-inline-progress-track{width:120px;height:3px;background:#0c1a24;border-radius:2px;overflow:hidden}.topbar-inline-progress-fill{height:100%;background:linear-gradient(90deg,#f60,#f90);transition:width .3s}.topbar-done{font-size:12px;color:#0f8;letter-spacing:.12em}.topbar-primary-btn,.topbar-secondary-btn,.sidebar-pill,.sidebar-control-list button,.inspector-header button,.graph-stage-toggle,.canvas-controls-panel button{font:inherit;cursor:pointer}.topbar-primary-btn,.topbar-secondary-btn{padding:8px 18px;font-size:13px;font-weight:700;letter-spacing:.15em;text-transform:uppercase;border:none;clip-path:polygon(6px 0%,100% 0%,calc(100% - 6px) 100%,0% 100%)}.topbar-primary-btn{background:linear-gradient(135deg,#f90,#f60);color:#060a0f}.topbar-secondary-btn{background:transparent;border:1px solid #1a2a38;color:#3a5a70;clip-path:none}.topbar-primary-btn:disabled,.topbar-secondary-btn:disabled,.canvas-controls-panel button:disabled,.graph-stage-toggle:disabled{cursor:not-allowed;opacity:.45}.cloudwire-layout{display:flex;min-height:0;height:100%;overflow:hidden}.sidebar-shell,.inspector-shell{border-radius:0;padding:16px 0;overflow:hidden;display:flex;flex-direction:column;gap:16px;min-height:0;flex-shrink:0}.sidebar-shell{width:200px;border-right:1px solid #0f1e2a}.sidebar-block{padding-bottom:0;border-bottom:none}.sidebar-block-grow{min-height:0;flex:1 1 auto;display:flex;flex-direction:column}.sidebar-block+.sidebar-block{padding-top:14px;border-top:1px solid #0f1e2a}.inspector-shell{width:300px;padding:16px 18px 18px;border-left:1px solid #0f1e2a;background:#07090d}.sidebar-shell section{padding:0 14px}.sidebar-section-title{margin-bottom:10px}.sidebar-services-list,.sidebar-results-list,.inspector-meta-list,.inspector-connection-list{display:flex;flex-direction:column;gap:0;overflow:auto;min-height:0}.sidebar-service-main{display:inline-flex;align-items:center;gap:6px}.sidebar-filter-pill{background:transparent;border:1px solid #1e3040;color:#5a7a90;font-family:inherit;font-size:12px;letter-spacing:.1em;padding:4px 10px;cursor:pointer;transition:all .15s;display:flex;justify-content:space-between;align-items:center;width:calc(100% + -0px);margin:2px 0;border-radius:2px}.sidebar-filter-pill.active,.sidebar-filter-pill:hover{background:#0d2030;border-color:#f90;color:#f90}.sidebar-filter-pill:not(.active) .sidebar-row-count{opacity:.6}.sidebar-row-count{font-size:11px;opacity:.6}.sidebar-service-icon,.inspector-icon{display:inline-flex;align-items:center;justify-content:center}.sidebar-service-icon svg{width:13px;height:13px}.sidebar-stats-list{display:flex;flex-direction:column;gap:0}.sidebar-stat-row{display:flex;justify-content:space-between;font-size:12px;color:#3a6a80;margin-bottom:6px}.sidebar-stat-row strong,.sidebar-empty-state,.inspector-empty,.inspector-id,.graph-stage-error{color:#3a6a80;font-size:12px}.sidebar-stat-row strong{color:#c8a860;font-weight:500}.sidebar-control-list{display:flex;flex-direction:column;gap:0}.sidebar-control-link{font-family:inherit;font-size:12px;color:#3a6080;padding:5px 8px;cursor:pointer;border-left:2px solid transparent;transition:all .15s;text-align:left;background:transparent;border-top:none;border-right:none;border-bottom:none}.sidebar-control-link:hover{color:#c8a860;border-left-color:#f90}.sidebar-search-input{font-family:inherit;font-size:12px;border-bottom:1px solid #1a2a38;margin-bottom:8px;padding-left:0;padding-right:0;border-top:none;border-left:none;border-right:none;background:transparent;color:#c8d8e8}.sidebar-search-input::-moz-placeholder{color:#294355}.sidebar-search-input::placeholder{color:#294355}.sidebar-search-section{min-height:0;display:flex;flex-direction:column;overflow:hidden}.sidebar-results-list{min-height:0;flex:1 1 auto}.sidebar-result-row{width:100%;display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:6px 0;border:none;background:transparent;color:#8aacbe;text-align:left;cursor:pointer}.sidebar-result-row:hover,.sidebar-result-row.active{color:#f90}.sidebar-result-row strong{font-size:12px;font-weight:400;color:inherit}.sidebar-result-row span{font-size:11px;color:#3a5a70;text-transform:uppercase;letter-spacing:.1em;opacity:.7}.sidebar-empty-state{padding:6px 0;color:#3a6a80;font-size:12px}.graph-stage-shell{flex:1 1 auto;border-radius:0;padding:0;display:block;min-height:0;overflow:hidden;border-left:none;border-right:none;position:relative}.graph-stage-error{position:absolute;bottom:48px;left:16px;z-index:5;max-width:420px;padding:7px 12px;background:#ff3c3c1a;border:1px solid rgba(255,60,60,.3);color:#f88;font-size:12px;letter-spacing:.03em;border-radius:2px}.graph-stage-error--resource{bottom:92px;background:#ffa0001a;border-color:#ffa0004d;color:#ffb84d}.graph-stage-error--info{bottom:92px;background:#00b4e01a;border-color:#00b4e04d;color:#00b4e0}.graph-canvas{position:relative;min-height:0;height:100%;overflow:hidden;border-radius:0;border:none;background:#060a0f}.graph-canvas-backdrop,.graph-canvas-grid{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none}.graph-canvas-backdrop{background:radial-gradient(circle at 18% 18%,rgba(255,153,0,.09),transparent 22%),radial-gradient(circle at 72% 28%,rgba(0,231,255,.08),transparent 19%),radial-gradient(circle at 46% 72%,rgba(123,45,139,.08),transparent 16%),linear-gradient(180deg,#070a0ff5,#04070b)}.graph-canvas-grid{background-image:linear-gradient(rgba(18,28,36,.55) 1px,transparent 1px),linear-gradient(90deg,rgba(18,28,36,.55) 1px,transparent 1px),linear-gradient(rgba(23,35,44,.74) 1px,transparent 1px),linear-gradient(90deg,rgba(23,35,44,.74) 1px,transparent 1px);background-size:20px 20px,20px 20px,100px 100px,100px 100px}.graph-canvas-header,.graph-canvas-corner,.graph-canvas-footer{position:absolute;z-index:2}.graph-canvas-footer{left:12px;bottom:12px;font-size:11px;color:#2a4a5a;letter-spacing:.1em;text-transform:uppercase}.graph-canvas-corner{right:12px;bottom:12px;display:flex;gap:8px;flex-wrap:wrap}.graph-svg{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%}.graph-annotation rect{fill:#060a0f8c;stroke:#0f2030;stroke-width:1}.graph-annotation text{fill:#294355;font-size:10px;letter-spacing:.12em;text-transform:uppercase}.graph-annotation-subtitle{fill:#213849;font-size:9px}.graph-legend{display:flex;gap:8px;flex-wrap:wrap;max-width:280px;justify-content:flex-end}.graph-legend-item{display:inline-flex;align-items:center;gap:4px;padding:0;border:none;background:transparent;font-size:11px;color:#3a5a70}.graph-legend-dot{width:8px;height:8px;border-radius:999px}.graph-legend-icon{display:inline-flex;align-items:center;justify-content:center}.graph-legend-icon svg{width:11px;height:11px}.graph-edge-path{filter:none}.graph-node-center-icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center}.graph-node-label{fill:#8aacbe;font-size:11px}.graph-node-kind{font-size:10px;opacity:.7}.inspector-shell{min-height:0;overflow:hidden}.inspector-meta-list,.inspector-connection-list{max-height:none}.inspector-header{display:flex;justify-content:space-between;align-items:flex-start;gap:8px}.inspector-header button{background:transparent;border:1px solid #1a2a38;color:#3a5a70;font-size:12px;padding:4px 8px}.inspector-header h2{margin:4px 0 0;color:#e8d8b8;font-size:17px;line-height:1.2}.inspector-identity{display:flex;align-items:center;margin-top:10px;gap:6px}.inspector-status-row{margin-top:10px}.inspector-status-pill{display:inline-flex;align-items:center;gap:5px;background:#0a1820;padding:4px 8px;border:1px solid;font-size:11px;letter-spacing:.1em}.inspector-status-dot{width:5px;height:5px;border-radius:50%;display:inline-block}.inspector-icon{width:auto;height:auto;padding:2px 8px;font-size:11px;background:#0c1820;border:1px solid;color:#2a4a5a}.inspector-chip-row{margin-top:10px;display:flex;gap:6px;flex-wrap:wrap}.inspector-chip{font-size:11px;color:#2a4a5a;background:#0c1820;padding:2px 8px;border:1px solid #1a2a38}.inspector-accent{height:2px;margin:12px -18px}.inspector-meta-row{display:flex;gap:8px;justify-content:space-between;padding:5px 0;border-bottom:1px solid #0d2030;font-size:13px}.inspector-meta-row span{color:#3a6a80;padding-right:10px}.inspector-meta-row strong{color:#c8b880;text-align:right;font-weight:500;word-break:break-word}.inspector-connection-row{justify-content:flex-start;width:100%;display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;border:none;background:transparent;border-bottom:1px solid #0a1420;text-align:left}.inspector-connection-row div{display:flex;flex-direction:column;gap:4px}.inspector-connection-arrow{font-size:12px;min-width:16px}.inspector-connection-row strong{color:#8aacbe;font-size:12px;font-weight:400}.inspector-connection-row span{color:#3a5a70;font-size:12px;text-transform:uppercase;letter-spacing:.1em}@media(max-width:1440px){.cloudwire-layout{gap:0}}@media(max-width:1200px){.cloudwire-layout{display:grid;grid-template-columns:200px minmax(0,1fr);position:relative}.inspector-shell{position:absolute;right:0;top:0;bottom:0;z-index:20;border-left:1px solid #1a2a38;box-shadow:-8px 0 24px #00000080}}.graph-stage-warnings{position:absolute;bottom:48px;left:16px;right:16px;z-index:5;background:#b478001f;border:1px solid rgba(255,153,0,.3);color:#c80;font-size:11px;letter-spacing:.08em;padding:5px 10px;border-radius:2px}.graph-stage-loading{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#3a5a70;font-size:13px;letter-spacing:.15em;text-transform:uppercase}.graph-empty-state{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;max-width:360px;pointer-events:none}.graph-empty-title{font-size:14px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;color:#3a5a70;margin-bottom:10px}.graph-empty-hint{font-size:12px;color:#2a3f4f;line-height:1.6;letter-spacing:.03em}.graph-empty-hint strong{color:#4a7a90;font-weight:600}.layout-select-wrap{position:relative;flex-shrink:0}.layout-select-trigger{display:flex;align-items:center;gap:7px;height:32px;padding:0 10px 0 12px;background:#0c1820;border:1px solid #1a2a38;color:#4a7a90;font:inherit;font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;cursor:pointer;transition:border-color .15s,color .15s,background .15s;white-space:nowrap}.layout-select-trigger:hover,.layout-select-trigger.open{border-color:#f90;color:#f90;background:#0f1e28}.layout-select-trigger-icon{font-size:13px;line-height:1}.layout-select-caret{font-size:8px;opacity:.55;margin-left:2px}.layout-select-panel{position:absolute;top:calc(100% + 5px);right:0;z-index:300;width:180px;background:#070d14;border:1px solid #1a2a38;box-shadow:0 12px 40px #000000b3,0 0 0 1px #ff990012,inset 0 1px #ffffff08;padding:4px 0}.layout-select-item{display:flex;align-items:center;gap:10px;padding:8px 14px;cursor:pointer;font-size:12px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:#5a7a8a;transition:background .1s,color .1s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.layout-select-item:hover{background:#ffffff08;color:#a0c0d0}.layout-select-item.active{color:#f90;background:#ff99000f}.layout-select-item-icon{font-size:14px;line-height:1;flex-shrink:0}.layout-select-item-check{margin-left:auto;font-size:10px;color:#f90}.topbar-force-refresh-label{display:flex;align-items:center;gap:5px;font-size:12px;color:#3a5a70;cursor:pointer;white-space:nowrap}.topbar-force-refresh-label input[type=checkbox]{accent-color:#ff9900;cursor:pointer}.topbar-warn-count{color:#c80}.svc-select-wrap{position:relative;flex-shrink:0}.svc-select-trigger{display:flex;align-items:center;gap:8px;height:32px;padding:0 10px 0 12px;background:#0c1820;border:1px solid #1a2a38;color:#4a7a90;font:inherit;font-size:12px;font-weight:600;letter-spacing:.06em;cursor:pointer;transition:border-color .15s,color .15s,background .15s;white-space:nowrap;min-width:148px;max-width:220px}.svc-select-trigger:hover,.svc-select-trigger.open{border-color:#f90;color:#f90;background:#0f1e28}.svc-select-trigger-dots{display:flex;align-items:center;gap:3px;flex-shrink:0}.svc-select-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.svc-select-label{flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis}.svc-select-caret{font-size:8px;opacity:.55;flex-shrink:0;margin-left:2px}.svc-select-panel{position:absolute;top:calc(100% + 5px);right:0;z-index:300;width:270px;background:#070d14;border:1px solid #1a2a38;box-shadow:0 12px 40px #000000b3,0 0 0 1px #ff990012,inset 0 1px #ffffff08;display:flex;flex-direction:column}.svc-select-actions{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid #0f1e2a}.svc-select-action-btn{height:22px;padding:0 10px;background:transparent;border:1px solid #1a2a38;color:#f90;font:inherit;font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;cursor:pointer;transition:background .12s,border-color .12s}.svc-select-action-btn:hover{background:#ff99001a;border-color:#ff990073}.svc-select-count{margin-left:auto;font-size:10px;color:#2a4a5a;letter-spacing:.1em;white-space:nowrap}.svc-select-list{overflow-y:auto;max-height:340px;padding:4px 0 8px;scrollbar-width:thin;scrollbar-color:#1a2a38 transparent}.svc-select-list::-webkit-scrollbar{width:4px}.svc-select-list::-webkit-scrollbar-track{background:transparent}.svc-select-list::-webkit-scrollbar-thumb{background:#1a2a38;border-radius:2px}.svc-select-group{padding:8px 0 2px}.svc-select-group+.svc-select-group{border-top:1px solid #0a1520}.svc-select-group-label{display:flex;align-items:center;gap:6px;padding:0 12px 5px;font-size:9px;font-weight:700;letter-spacing:.2em;text-transform:uppercase;color:#2a4a5a}.svc-select-group-label:after{content:"";flex:1;height:1px;background:linear-gradient(90deg,#0f1e2a,transparent)}.svc-select-item{display:flex;align-items:center;gap:9px;padding:5px 12px 5px 14px;cursor:pointer;font-size:12px;color:#5a7a8a;letter-spacing:.03em;transition:background .1s,color .1s;-webkit-user-select:none;-moz-user-select:none;user-select:none}.svc-select-item:hover{background:#ffffff08;color:#a0c0d0}.svc-select-item.checked{color:#c8d8e8}.svc-select-item.checked:hover{background:#ff99000f}.svc-select-item-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;opacity:.5;transition:opacity .1s}.svc-select-item.checked .svc-select-item-dot{opacity:1}.svc-select-checkbox{display:none}.svc-select-item-check{width:14px;height:14px;flex-shrink:0;border:1px solid #1a3040;background:#0a1520;border-radius:2px;display:flex;align-items:center;justify-content:center;transition:background .12s,border-color .12s}.svc-select-item.checked .svc-select-item-check{background:#ff990026;border-color:#f90}.svc-select-item-checkmark{display:none;width:8px;height:8px}.svc-select-item.checked .svc-select-item-checkmark{display:block}.sidebar-search-note{font-size:11px;color:#3a5a6a;margin-bottom:4px;letter-spacing:.05em}.error-boundary-shell{height:100vh;display:flex;align-items:center;justify-content:center;background:#05080d;color:#d6e3ef}.error-boundary-inner{text-align:center;max-width:400px;padding:32px;border:1px solid #1a2a38;background:#070d14}.error-boundary-title{font-size:18px;font-weight:600;color:#e8c888;margin-bottom:12px;letter-spacing:.1em;text-transform:uppercase}.error-boundary-message{font-size:14px;color:#5a8a9a;margin-bottom:20px;word-break:break-word}.error-boundary-reset{font:inherit;font-size:13px;font-weight:700;letter-spacing:.15em;text-transform:uppercase;padding:8px 20px;background:linear-gradient(135deg,#f90,#f60);color:#060a0f;border:none;cursor:pointer;clip-path:polygon(6px 0%,100% 0%,calc(100% - 6px) 100%,0% 100%)}.minimap-shell{background:#05080de0;border:1px solid #0f1e2a;padding:4px 6px 6px;display:flex;flex-direction:column;gap:2px}.minimap-label{font-size:9px;color:#2a4a5a;letter-spacing:.18em;text-transform:uppercase}.minimap-svg{display:block;cursor:crosshair}.focus-mode-bar{position:absolute;top:8px;left:50%;transform:translate(-50%);z-index:10;display:flex;align-items:center;gap:8px;background:#05080deb;border:1px solid #1a2a38;padding:5px 12px;white-space:nowrap}.focus-mode-label{font-size:11px;color:#3a6a80;letter-spacing:.1em;text-transform:uppercase}.focus-mode-controls{display:flex;align-items:center;gap:4px}.focus-depth-btn{font:inherit;font-size:11px;letter-spacing:.08em;background:transparent;border:1px solid #1a2a38;color:#3a6a80;padding:3px 8px;cursor:pointer;transition:all .12s}.focus-depth-btn.active,.focus-depth-btn:hover{border-color:#f90;color:#f90}.focus-toggle-btn{font:inherit;font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;background:transparent;border:1px solid #2a4a5a;color:#4a8a9a;padding:3px 10px;cursor:pointer;transition:all .12s}.focus-toggle-btn.active{background:#ff99001f;border-color:#f90;color:#f90}.focus-toggle-btn:hover{border-color:#f90;color:#f90}.graph-cluster-count{fill:#e8c888;font-size:17px;font-weight:700;dominant-baseline:middle;text-anchor:middle}.graph-cluster-label{fill:#5a8a9a;font-size:10px;letter-spacing:.12em;text-transform:uppercase;dominant-baseline:middle;text-anchor:middle}.sidebar-pill-row{display:flex;align-items:center;gap:2px}.sidebar-filter-pill-inline{flex:1 1 auto;width:auto}.sidebar-cluster-btn{font:inherit;font-size:13px;background:transparent;border:1px solid #1a2a38;color:#3a5a70;padding:2px 5px;cursor:pointer;transition:all .12s;flex-shrink:0;line-height:1}.sidebar-cluster-btn:hover,.sidebar-cluster-btn.active{border-color:#f90;color:#f90}.sidebar-isolated-toggle{width:100%;display:flex;justify-content:space-between;align-items:center;font:inherit;font-size:12px;letter-spacing:.08em;background:transparent;border:1px solid #1a2a38;color:#3a6a80;padding:5px 10px;cursor:pointer;transition:all .12s}.sidebar-isolated-toggle.active{border-color:#2a5a6a;color:#5a9aaa;background:#2a5a6a1a}.sidebar-isolated-toggle:hover{border-color:#f90;color:#f90}.graph-annotation.lane-trigger rect{fill:#ff99000a;stroke:#ff99002e}.graph-annotation.lane-trigger text{fill:#ff990073}.graph-annotation.lane-queue rect{fill:#ff4f8b0a;stroke:#ff4f8b2e}.graph-annotation.lane-queue text{fill:#ff4f8b73}.graph-annotation.lane-processor rect{fill:#00e7ff0a;stroke:#00e7ff2e}.graph-annotation.lane-processor text{fill:#00e7ff73}.graph-annotation.lane-storage rect{fill:#7b2d8b0d;stroke:#7b2d8b33}.graph-annotation.lane-storage text{fill:#7b2d8b99}.graph-annotation.lane-unknown rect{fill:#6f859608;stroke:#6f85961f}.graph-annotation.lane-unknown text{fill:#6f859659}.graph-toolbar{position:absolute;top:10px;right:12px;z-index:10;display:flex;gap:6px}.graph-toolbar-btn{font:inherit;font-size:10px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;background:#05080de0;border:1px solid #1a2a38;color:#3a6a80;padding:5px 12px;cursor:pointer;transition:all .12s}.graph-toolbar-btn:hover,.graph-toolbar-btn.active{border-color:#f90;color:#f90;background:#ff990014}.summary-panel{position:absolute;top:48px;right:12px;z-index:15;width:340px;background:#05080df5;border:1px solid #1a2a38;padding:14px 16px 16px;box-shadow:0 8px 32px #0009}.summary-panel-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.summary-panel-title{font-size:10px;font-weight:700;letter-spacing:.18em;color:#c8a860;text-transform:uppercase}.summary-panel-close{font:inherit;font-size:12px;background:transparent;border:none;color:#3a6a80;cursor:pointer;padding:2px 6px}.summary-panel-close:hover{color:#f90}.summary-panel-text{font-size:12px;color:#7aacbe;line-height:1.65;margin:0 0 12px}.summary-patterns{border-top:1px solid #0f1e2a;padding-top:10px}.summary-patterns-title{font-size:10px;letter-spacing:.15em;color:#2a4a5a;text-transform:uppercase;margin-bottom:8px}.summary-pattern-row{display:flex;flex-direction:column;gap:2px;padding:6px 0;border-bottom:1px solid #0a1420}.summary-pattern-name{font-size:11px;font-weight:600;color:#f90;letter-spacing:.05em}.summary-pattern-desc{font-size:10px;color:#4a7a8a}.layout-loading-overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:20;background:#05080db8;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;color:#4a8a9a;font-size:12px;letter-spacing:.18em;text-transform:uppercase;pointer-events:none}.layout-loading-spinner{width:28px;height:28px;border:2px solid #1a2a38;border-top-color:#f90;border-radius:50%;animation:layout-spin .75s linear infinite}@keyframes layout-spin{to{transform:rotate(360deg)}}.focus-mode-bar .focus-toggle-btn.path-active{background:#00e7ff1a;border-color:#00e7ff;color:#00e7ff}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:IBM Plex Mono,ui-monospace,SFMono-Regular,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.flex{display:flex}.hidden{display:none}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}*{box-sizing:border-box}html,body,#root{width:100%;height:100%;margin:0;font-family:IBM Plex Mono,ui-monospace,SFMono-Regular,monospace;background:#05080d;color:#d6e3ef}button,input,select{font:inherit}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:#04080cf5}::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#ff99006b,#00e7ff57);border-radius:999px}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>CloudWire</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-CByMF4j6.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-lik1Sxh5.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="root"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudwire
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Scan and visualize your AWS infrastructure as an interactive graph
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/hisingh_gwre/cloudwire
|
|
7
|
+
Project-URL: Issues, https://github.com/hisingh_gwre/cloudwire/issues
|
|
8
|
+
Keywords: aws,cloud,visualization,graph,infrastructure
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
|
+
Classifier: Topic :: System :: Systems Administration
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: fastapi>=0.115
|
|
24
|
+
Requires-Dist: uvicorn[standard]>=0.34
|
|
25
|
+
Requires-Dist: boto3>=1.37
|
|
26
|
+
Requires-Dist: botocore>=1.37
|
|
27
|
+
Requires-Dist: networkx>=3.4
|
|
28
|
+
Requires-Dist: pydantic>=2.11
|
|
29
|
+
Requires-Dist: click>=8.1
|
|
30
|
+
|
|
31
|
+
# cloudwire
|
|
32
|
+
|
|
33
|
+
Scan your AWS account and visualize resource dependencies as an interactive graph — directly in your browser, running entirely on your local machine.
|
|
34
|
+
|
|
35
|
+
No data leaves your system. AWS credentials never leave your terminal. The graph is built locally using your existing credential chain (`~/.aws/credentials`, `aws sso login`, `saml2aws`, `aws-vault` — all work out of the box).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install cloudwire
|
|
43
|
+
cloudwire
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
That's it. The browser opens automatically at `http://localhost:8080`.
|
|
47
|
+
|
|
48
|
+
> **Requirements:** Python 3.9+ and valid AWS credentials configured locally.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## What it looks like
|
|
53
|
+
|
|
54
|
+
- Dark hacker-aesthetic graph canvas
|
|
55
|
+
- Nodes represent AWS resources — Lambda functions, SQS queues, API Gateways, RDS instances, S3 buckets, and more
|
|
56
|
+
- Edges represent relationships and data flow between resources
|
|
57
|
+
- Click any node to inspect its attributes and connected resources
|
|
58
|
+
- Search, filter by service, highlight upstream/downstream blast radius
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Supported services
|
|
63
|
+
|
|
64
|
+
| Service | Scanner |
|
|
65
|
+
|---------|---------|
|
|
66
|
+
| API Gateway | Dedicated |
|
|
67
|
+
| Lambda | Dedicated (with state) |
|
|
68
|
+
| SQS | Dedicated |
|
|
69
|
+
| SNS | Dedicated |
|
|
70
|
+
| EventBridge | Dedicated |
|
|
71
|
+
| DynamoDB | Dedicated (with state) |
|
|
72
|
+
| EC2 | Dedicated (with state) |
|
|
73
|
+
| ECS | Dedicated |
|
|
74
|
+
| S3 | Dedicated |
|
|
75
|
+
| RDS | Dedicated (with state) |
|
|
76
|
+
| Step Functions | Dedicated |
|
|
77
|
+
| Kinesis | Dedicated |
|
|
78
|
+
| IAM | Dedicated |
|
|
79
|
+
| Cognito | Dedicated |
|
|
80
|
+
| CloudFront | Dedicated (with state) |
|
|
81
|
+
| ElastiCache | Dedicated (with state) |
|
|
82
|
+
| Glue | Dedicated |
|
|
83
|
+
| AppSync | Dedicated |
|
|
84
|
+
| Everything else | Generic (tagged resources only) |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Project structure
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
cloudwire/ # Python package (the distributable unit)
|
|
92
|
+
├── __init__.py # Package version
|
|
93
|
+
├── cli.py # `cloudwire` CLI entry point (click)
|
|
94
|
+
├── static/ # Built React app (populated by `make build`)
|
|
95
|
+
│ ├── index.html
|
|
96
|
+
│ └── assets/
|
|
97
|
+
└── app/ # FastAPI backend
|
|
98
|
+
├── main.py # App factory, API routes (/api/*), static serving
|
|
99
|
+
├── models.py # Pydantic request/response models
|
|
100
|
+
├── scanner.py # boto3 AWS scanner — one function per service
|
|
101
|
+
├── scan_jobs.py # Async job store with progress tracking
|
|
102
|
+
└── graph_store.py # networkx graph with thread-safe mutations
|
|
103
|
+
|
|
104
|
+
frontend/ # React + Vite source (compiled into cloudwire/static/)
|
|
105
|
+
├── src/
|
|
106
|
+
│ ├── pages/CloudWirePage.jsx # Main page — orchestrates all state
|
|
107
|
+
│ ├── components/
|
|
108
|
+
│ │ ├── graph/ # GraphCanvas, GraphNode, GraphEdge, Minimap, Legend
|
|
109
|
+
│ │ └── layout/ # TopBar, ServiceSidebar, InspectorPanel
|
|
110
|
+
│ ├── hooks/
|
|
111
|
+
│ │ ├── useScanPolling.js # Scan lifecycle, polling, graph data state
|
|
112
|
+
│ │ └── useGraphViewport.js # Pan/zoom viewport state
|
|
113
|
+
│ ├── lib/
|
|
114
|
+
│ │ ├── graphTransforms.js # Layout algorithms (circular, flow, swimlane)
|
|
115
|
+
│ │ ├── serviceVisuals.jsx # Service icon + color map
|
|
116
|
+
│ │ └── awsRegions.js # AWS region list
|
|
117
|
+
│ └── styles/graph.css # All UI styles
|
|
118
|
+
├── vite.config.js # base: "./", outDir: ../cloudwire/static, dev proxy
|
|
119
|
+
└── package.json
|
|
120
|
+
|
|
121
|
+
.github/workflows/publish.yml # CI: build + publish to PyPI on version tag push
|
|
122
|
+
pyproject.toml # Package metadata, dependencies, entry point
|
|
123
|
+
Makefile # make build / make dev / make clean
|
|
124
|
+
.python-version # Pins Python 3.11 for consistent builds
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
130
|
+
|
|
131
|
+
### Prerequisites
|
|
132
|
+
|
|
133
|
+
- Python 3.9+ (3.11 recommended)
|
|
134
|
+
- Node.js 18+
|
|
135
|
+
- AWS credentials configured (any method)
|
|
136
|
+
|
|
137
|
+
### Set up the dev environment
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
git clone https://github.com/hisingh_gwre/cloudwire
|
|
141
|
+
cd cloudwire
|
|
142
|
+
|
|
143
|
+
# Python
|
|
144
|
+
python3 -m venv .venv
|
|
145
|
+
source .venv/bin/activate
|
|
146
|
+
pip install -e .
|
|
147
|
+
|
|
148
|
+
# Frontend
|
|
149
|
+
cd frontend && npm install
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Run in development mode
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
make dev
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This starts the FastAPI backend on `:8000` (with `--reload`) and the Vite dev server on `:5173` concurrently. The Vite dev server proxies all `/api/*` requests to the backend — no CORS config needed.
|
|
159
|
+
|
|
160
|
+
### Making changes
|
|
161
|
+
|
|
162
|
+
| Area | Where to edit |
|
|
163
|
+
|------|--------------|
|
|
164
|
+
| Add a new AWS service scanner | `cloudwire/app/scanner.py` → add a `_scan_<service>` method and register it in `self.service_scanners` |
|
|
165
|
+
| Change graph layout | `frontend/src/lib/graphTransforms.js` |
|
|
166
|
+
| Add a new UI component | `frontend/src/components/` |
|
|
167
|
+
| Change API routes | `cloudwire/app/main.py` — all routes are under the `/api` prefix |
|
|
168
|
+
| Change CLI options | `cloudwire/cli.py` |
|
|
169
|
+
|
|
170
|
+
### Before opening a PR
|
|
171
|
+
|
|
172
|
+
- Run a scan against a real (or mocked) AWS account and confirm the graph renders
|
|
173
|
+
- Make sure `make build` completes without errors
|
|
174
|
+
- Keep PRs focused — one feature or fix per PR
|
|
175
|
+
|
|
176
|
+
### Code style
|
|
177
|
+
|
|
178
|
+
- Python: standard library imports first, then third-party, then local. No formatter enforced yet.
|
|
179
|
+
- JavaScript: no linter enforced yet. Match the style of the surrounding file.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Links
|
|
184
|
+
|
|
185
|
+
- [Usage & setup guide](docs/USAGE.md)
|
|
186
|
+
- [Release guide for maintainers](docs/RELEASING.md)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
cloudwire/__init__.py,sha256=apOVKn2E4dUxBuPEBOyPEtx-Qo-VVEv1J1p0Mhr-6i8,87
|
|
2
|
+
cloudwire/cli.py,sha256=XeYvcL367LPgqLg46RnFT1wBLyDRd9zbcJl5hiGzQNU,3233
|
|
3
|
+
cloudwire/app/__init__.py,sha256=wNbizpJ_aRNo8vwKB66gymXbKAGFIKHGMosStf9Q15g,33
|
|
4
|
+
cloudwire/app/graph_store.py,sha256=NK69-qYuJ8PKMJ3IKEvakJ5d6TeKwOHwonr8ZcM_DEw,3382
|
|
5
|
+
cloudwire/app/main.py,sha256=0b-avcw10d0renEVz2UXsnD-UyDkjdRTm3GE1IuxDiw,15533
|
|
6
|
+
cloudwire/app/models.py,sha256=PbFnqn_MNVxGZh2hDPIIHiqlF9AjAAdFixYfUzw9Arg,2211
|
|
7
|
+
cloudwire/app/scan_jobs.py,sha256=YYemUXi5BZvR8sBfuEAkjIjfOvHAs-FjourSYTA1blg,13282
|
|
8
|
+
cloudwire/app/scanner.py,sha256=SEWMacilf0C_mHPwEXCNKS9pSgdX3O62TZTxa-EPv8A,51634
|
|
9
|
+
cloudwire/static/index.html,sha256=Zs3sPTNTMni6ex9lrGhnvaQwN30ZmaDf0apCMX-2rTI,396
|
|
10
|
+
cloudwire/static/assets/index-CByMF4j6.js,sha256=t7kxbGuF8e_2M-rxSPzojpEiWU9sGazuUP-1g4EW_aw,204498
|
|
11
|
+
cloudwire/static/assets/index-lik1Sxh5.css,sha256=A7n6DGiw2AVOBGmYpgOspBbhNnOEaAOcXLJGYCapuQk,28572
|
|
12
|
+
cloudwire-0.1.0.dist-info/METADATA,sha256=mlWX4nJjM-o1D6aMTjLPDbBqTY8J5sK8pC_cmWAED4Q,6514
|
|
13
|
+
cloudwire-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
14
|
+
cloudwire-0.1.0.dist-info/entry_points.txt,sha256=pVoiRkRueQLNFlIeImNIWKfhVSo3j9TEn0y2UWlsbN8,49
|
|
15
|
+
cloudwire-0.1.0.dist-info/top_level.txt,sha256=aDxE_-CdonPoAamgrbCUhQ3sQ3tfI_VAJJN1dBsA4QU,10
|
|
16
|
+
cloudwire-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cloudwire
|