@bonyadnouri/autoend 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -38
- package/dist/cli.js +5 -3
- package/dist/cli.js.map +1 -1
- package/dist/explore/explorer.d.ts +4 -1
- package/dist/explore/explorer.js +62 -23
- package/dist/explore/explorer.js.map +1 -1
- package/dist/map/flow-map.d.ts +46 -0
- package/dist/map/flow-map.js +99 -6
- package/dist/map/flow-map.js.map +1 -1
- package/dist/replay/replay.d.ts +13 -2
- package/dist/replay/replay.js +92 -11
- package/dist/replay/replay.js.map +1 -1
- package/dist/report/artifact.d.ts +7 -1
- package/dist/report/artifact.js +10 -0
- package/dist/report/artifact.js.map +1 -1
- package/dist/report/types.d.ts +62 -0
- package/dist/run/run.d.ts +6 -1
- package/dist/run/run.js +64 -9
- package/dist/run/run.js.map +1 -1
- package/dist/run/sensitive-env.d.ts +24 -0
- package/dist/run/sensitive-env.js +45 -0
- package/dist/run/sensitive-env.js.map +1 -0
- package/dist/setup/wizard.js +5 -3
- package/dist/setup/wizard.js.map +1 -1
- package/dist/spa/assets/index-BYZk0rZl.js +264 -0
- package/dist/spa/assets/index-D8cAgArG.css +1 -0
- package/dist/spa/index.html +14 -0
- package/dist/spa/logo.svg +7 -0
- package/dist/viewer/server.d.ts +7 -6
- package/dist/viewer/server.js +168 -26
- package/dist/viewer/server.js.map +1 -1
- package/package.json +17 -4
- package/dist/viewer/html.d.ts +0 -7
- package/dist/viewer/html.js +0 -240
- package/dist/viewer/html.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap";*,: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:Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;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:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,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}html,body,#root{height:100%}body{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1));-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.card{border-radius:.75rem;border-width:1px;--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-shadow: 0 1px 2px 0 rgba(16, 24, 40, .04), 0 1px 3px 0 rgba(16, 24, 40, .08);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color), 0 1px 3px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.card-hover{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.card-hover:hover{--tw-translate-y: -.125rem;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));--tw-shadow: 0 4px 8px -2px rgba(16, 24, 40, .08), 0 2px 4px -2px rgba(16, 24, 40, .06);--tw-shadow-colored: 0 4px 8px -2px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.pill{display:inline-flex;align-items:center;gap:.375rem;border-radius:9999px;padding:.25rem .625rem;font-size:.75rem;line-height:1rem;font-weight:500}.btn-primary{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:.5rem;padding:.625rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:600;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.btn-primary:focus{outline:2px solid transparent;outline-offset:2px}.btn-primary:focus-visible{--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(2px + 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);--tw-ring-opacity: 1;--tw-ring-color: rgb(89 139 255 / var(--tw-ring-opacity, 1));--tw-ring-offset-width: 2px}.btn-primary:disabled{cursor:not-allowed;opacity:.6}.btn-primary{--tw-bg-opacity: 1;background-color:rgb(31 79 245 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.btn-primary:hover{--tw-bg-opacity: 1;background-color:rgb(23 61 225 / var(--tw-bg-opacity, 1))}.btn-secondary{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;border-radius:.5rem;padding:.625rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:600;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.btn-secondary:focus{outline:2px solid transparent;outline-offset:2px}.btn-secondary:focus-visible{--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(2px + 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);--tw-ring-opacity: 1;--tw-ring-color: rgb(89 139 255 / var(--tw-ring-opacity, 1));--tw-ring-offset-width: 2px}.btn-secondary:disabled{cursor:not-allowed;opacity:.6}.btn-secondary{border-width:1px;--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1));--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.btn-secondary:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.section-title{font-size:.875rem;line-height:1.25rem;font-weight:600;text-transform:uppercase;letter-spacing:.025em;--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.pointer-events-none{pointer-events:none}.static{position:static}.absolute{position:absolute}.relative{position:relative}.left-2\.5{left:.625rem}.right-0{right:0}.top-1\/2{top:50%}.z-20{z-index:20}.m-3{margin:.75rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.aspect-video{aspect-ratio:16 / 9}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-80{max-height:20rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-40{width:10rem}.w-44{width:11rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-2xl{max-width:42rem}.max-w-7xl{max-width:80rem}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y: -50%;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))}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-3{row-gap:.75rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.625rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.625rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse: 0;border-top-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px * var(--tw-divide-y-reverse))}.divide-slate-100>:not([hidden])~:not([hidden]){--tw-divide-opacity: 1;border-color:rgb(241 245 249 / var(--tw-divide-opacity, 1))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-slate-100{--tw-border-opacity: 1;border-color:rgb(241 245 249 / var(--tw-border-opacity, 1))}.border-slate-200{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.border-status-fail\/30{border-color:#dc26264d}.border-status-pass\/30{border-color:#16a34a4d}.border-l-status-ai{--tw-border-opacity: 1;border-left-color:rgb(37 99 235 / var(--tw-border-opacity, 1))}.bg-amber-100{--tw-bg-opacity: 1;background-color:rgb(254 243 199 / var(--tw-bg-opacity, 1))}.bg-black{--tw-bg-opacity: 1;background-color:rgb(0 0 0 / var(--tw-bg-opacity, 1))}.bg-brand-50{--tw-bg-opacity: 1;background-color:rgb(238 244 255 / var(--tw-bg-opacity, 1))}.bg-brand-600{--tw-bg-opacity: 1;background-color:rgb(31 79 245 / var(--tw-bg-opacity, 1))}.bg-emerald-100{--tw-bg-opacity: 1;background-color:rgb(209 250 229 / var(--tw-bg-opacity, 1))}.bg-rose-100{--tw-bg-opacity: 1;background-color:rgb(255 228 230 / var(--tw-bg-opacity, 1))}.bg-sky-100{--tw-bg-opacity: 1;background-color:rgb(224 242 254 / var(--tw-bg-opacity, 1))}.bg-slate-100{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.bg-slate-200{--tw-bg-opacity: 1;background-color:rgb(226 232 240 / var(--tw-bg-opacity, 1))}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.bg-slate-900{--tw-bg-opacity: 1;background-color:rgb(15 23 42 / var(--tw-bg-opacity, 1))}.bg-status-ai{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-status-aiBg{--tw-bg-opacity: 1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-status-aiBg\/40{background-color:#dbeafe66}.bg-status-fail{--tw-bg-opacity: 1;background-color:rgb(220 38 38 / var(--tw-bg-opacity, 1))}.bg-status-failBg{--tw-bg-opacity: 1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-status-failBg\/40{background-color:#fee2e266}.bg-status-passBg{--tw-bg-opacity: 1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-status-passBg\/40{background-color:#dcfce766}.bg-status-warn{--tw-bg-opacity: 1;background-color:rgb(217 119 6 / var(--tw-bg-opacity, 1))}.bg-status-warnBg{--tw-bg-opacity: 1;background-color:rgb(254 243 199 / var(--tw-bg-opacity, 1))}.bg-violet-100{--tw-bg-opacity: 1;background-color:rgb(237 233 254 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pl-8{padding-left:2rem}.pr-2{padding-right:.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-amber-700{--tw-text-opacity: 1;color:rgb(180 83 9 / var(--tw-text-opacity, 1))}.text-brand-600{--tw-text-opacity: 1;color:rgb(31 79 245 / var(--tw-text-opacity, 1))}.text-brand-700{--tw-text-opacity: 1;color:rgb(23 61 225 / var(--tw-text-opacity, 1))}.text-emerald-700{--tw-text-opacity: 1;color:rgb(4 120 87 / var(--tw-text-opacity, 1))}.text-rose-400{--tw-text-opacity: 1;color:rgb(251 113 133 / var(--tw-text-opacity, 1))}.text-rose-700{--tw-text-opacity: 1;color:rgb(190 18 60 / var(--tw-text-opacity, 1))}.text-sky-700{--tw-text-opacity: 1;color:rgb(3 105 161 / var(--tw-text-opacity, 1))}.text-slate-200{--tw-text-opacity: 1;color:rgb(226 232 240 / var(--tw-text-opacity, 1))}.text-slate-300{--tw-text-opacity: 1;color:rgb(203 213 225 / var(--tw-text-opacity, 1))}.text-slate-400{--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity, 1))}.text-slate-500{--tw-text-opacity: 1;color:rgb(100 116 139 / var(--tw-text-opacity, 1))}.text-slate-600{--tw-text-opacity: 1;color:rgb(71 85 105 / var(--tw-text-opacity, 1))}.text-slate-700{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.text-slate-800{--tw-text-opacity: 1;color:rgb(30 41 59 / var(--tw-text-opacity, 1))}.text-slate-900{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.text-status-ai{--tw-text-opacity: 1;color:rgb(37 99 235 / var(--tw-text-opacity, 1))}.text-status-fail{--tw-text-opacity: 1;color:rgb(220 38 38 / var(--tw-text-opacity, 1))}.text-status-pass{--tw-text-opacity: 1;color:rgb(22 163 74 / var(--tw-text-opacity, 1))}.text-status-warn{--tw-text-opacity: 1;color:rgb(217 119 6 / var(--tw-text-opacity, 1))}.text-violet-700{--tw-text-opacity: 1;color:rgb(109 40 217 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.line-through{text-decoration-line:line-through}.shadow-cardHover{--tw-shadow: 0 4px 8px -2px rgba(16, 24, 40, .08), 0 2px 4px -2px rgba(16, 24, 40, .06);--tw-shadow-colored: 0 4px 8px -2px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.ring-1{--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(1px + 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)}.ring-transparent{--tw-ring-color: transparent}.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)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:border-brand-200:hover{--tw-border-opacity: 1;border-color:rgb(188 210 255 / var(--tw-border-opacity, 1))}.hover\:border-slate-200:hover{--tw-border-opacity: 1;border-color:rgb(226 232 240 / var(--tw-border-opacity, 1))}.hover\:bg-brand-50\/40:hover{background-color:#eef4ff66}.hover\:bg-slate-100:hover{--tw-bg-opacity: 1;background-color:rgb(241 245 249 / var(--tw-bg-opacity, 1))}.hover\:bg-slate-50:hover{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity, 1))}.hover\:text-brand-700:hover{--tw-text-opacity: 1;color:rgb(23 61 225 / var(--tw-text-opacity, 1))}.hover\:text-slate-700:hover{--tw-text-opacity: 1;color:rgb(51 65 85 / var(--tw-text-opacity, 1))}.hover\:text-slate-900:hover{--tw-text-opacity: 1;color:rgb(15 23 42 / var(--tw-text-opacity, 1))}.focus\:border-brand-300:focus{--tw-border-opacity: 1;border-color:rgb(142 180 255 / var(--tw-border-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-2:focus{--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(2px + 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)}.focus\:ring-brand-100:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(217 230 255 / var(--tw-ring-opacity, 1))}.group:hover .group-hover\:ring-brand-200{--tw-ring-opacity: 1;--tw-ring-color: rgb(188 210 255 / var(--tw-ring-opacity, 1))}@media (min-width: 640px){.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:col-span-2{grid-column:span 2 / span 2}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="./logo.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>autoend — Run Report</title>
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-BYZk0rZl.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-D8cAgArG.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
|
2
|
+
<rect width="32" height="32" rx="8" fill="#3366ff"/>
|
|
3
|
+
<circle cx="11" cy="11" r="3" fill="#fff"/>
|
|
4
|
+
<circle cx="21" cy="11" r="3" fill="#fff"/>
|
|
5
|
+
<circle cx="16" cy="21" r="3" fill="#fff"/>
|
|
6
|
+
<path d="M11 11 L21 11 M11 11 L16 21 M21 11 L16 21" stroke="#fff" stroke-width="1.5" opacity="0.6"/>
|
|
7
|
+
</svg>
|
package/dist/viewer/server.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { type Server } from 'node:http';
|
|
2
2
|
/**
|
|
3
|
-
* The thin viewer over a Run artifact (ADR-0004):
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
3
|
+
* The thin viewer over a Run artifact (ADR-0004): serves the built lumen
|
|
4
|
+
* viewer (dist/spa) plus the artifact's files. Resolution actions
|
|
5
|
+
* (Dismiss / Reject / Suppress — one per Finding tier) are plain file edits
|
|
6
|
+
* against the repo's Flow Map; they only work when a repoRoot is provided
|
|
7
|
+
* (the Report is portable, the Flow Map is not). This server must stay dumb:
|
|
8
|
+
* no LLM, no agent execution.
|
|
8
9
|
*/
|
|
9
10
|
export interface Viewer {
|
|
10
11
|
url: string;
|
|
11
12
|
server: Server;
|
|
12
13
|
}
|
|
13
|
-
export declare function serveReport(artifactDir: string, port?: number): Promise<Viewer>;
|
|
14
|
+
export declare function serveReport(artifactDir: string, port?: number, repoRoot?: string): Promise<Viewer>;
|
package/dist/viewer/server.js
CHANGED
|
@@ -1,46 +1,188 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
2
|
+
import { createReadStream } from 'node:fs';
|
|
3
|
+
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { extname, isAbsolute, join, normalize } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { removeFlow } from '../map/flow-map.js';
|
|
7
|
+
import { readRunArtifact, updateFinding } from '../report/artifact.js';
|
|
8
|
+
/** dist/spa sits at the package root beside src/ and dist/ — this resolves identically from both. */
|
|
9
|
+
const VIEWER_DIST = fileURLToPath(new URL('../../dist/spa/', import.meta.url));
|
|
10
|
+
const CONTENT_TYPES = {
|
|
11
|
+
'.html': 'text/html; charset=utf-8',
|
|
12
|
+
'.js': 'text/javascript',
|
|
13
|
+
'.css': 'text/css',
|
|
14
|
+
'.svg': 'image/svg+xml',
|
|
15
|
+
'.png': 'image/png',
|
|
16
|
+
'.webm': 'video/webm',
|
|
17
|
+
};
|
|
18
|
+
function sendJson(res, status, body) {
|
|
19
|
+
res.writeHead(status, { 'content-type': 'application/json' });
|
|
20
|
+
res.end(JSON.stringify(body));
|
|
21
|
+
}
|
|
22
|
+
function sendStatus(res, status) {
|
|
23
|
+
res.writeHead(status);
|
|
24
|
+
res.end();
|
|
25
|
+
}
|
|
26
|
+
async function sendFile(res, path) {
|
|
27
|
+
let data;
|
|
28
|
+
try {
|
|
29
|
+
data = await readFile(path);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
sendStatus(res, 404);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
res.writeHead(200, { 'content-type': CONTENT_TYPES[extname(path)] ?? 'application/octet-stream' });
|
|
36
|
+
res.end(data);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Serve an Evidence file with HTTP Range support so Chrome can seek a <video>
|
|
40
|
+
* (it refuses to seek a source served without byte ranges). Streams only the
|
|
41
|
+
* requested slice; mirrors the dev middleware in viewer/vite.config.ts.
|
|
42
|
+
*/
|
|
43
|
+
async function sendEvidence(res, path, rangeHeader) {
|
|
44
|
+
let size;
|
|
45
|
+
try {
|
|
46
|
+
({ size } = await stat(path));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
sendStatus(res, 404);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const type = CONTENT_TYPES[extname(path)] ?? 'application/octet-stream';
|
|
53
|
+
const range = /^bytes=(\d+)-(\d*)$/.exec(rangeHeader ?? '');
|
|
54
|
+
if (range) {
|
|
55
|
+
const start = Number(range[1]);
|
|
56
|
+
const end = range[2] === '' ? size - 1 : Math.min(Number(range[2]), size - 1);
|
|
57
|
+
if (start > end || start >= size) {
|
|
58
|
+
res.writeHead(416, { 'content-range': `bytes */${size}` });
|
|
59
|
+
res.end();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
res.writeHead(206, {
|
|
63
|
+
'content-type': type,
|
|
64
|
+
'accept-ranges': 'bytes',
|
|
65
|
+
'content-range': `bytes ${start}-${end}/${size}`,
|
|
66
|
+
'content-length': end - start + 1,
|
|
67
|
+
});
|
|
68
|
+
createReadStream(path, { start, end }).pipe(res);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
res.writeHead(200, { 'content-type': type, 'accept-ranges': 'bytes', 'content-length': size });
|
|
72
|
+
createReadStream(path).pipe(res);
|
|
73
|
+
}
|
|
74
|
+
/** decodeURIComponent that reports malformed input instead of throwing. */
|
|
75
|
+
function decodePath(component) {
|
|
76
|
+
try {
|
|
77
|
+
return decodeURIComponent(component);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Suppress (CONTEXT.md): record the Advisory's title so future Runs stop re-reporting it. */
|
|
84
|
+
async function suppressTitle(repoRoot, title) {
|
|
85
|
+
const file = join(repoRoot, '.autoend', 'suppressed.json');
|
|
86
|
+
let advisories = [];
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(await readFile(file, 'utf8'));
|
|
89
|
+
if (Array.isArray(parsed.advisories)) {
|
|
90
|
+
advisories = parsed.advisories.filter((t) => typeof t === 'string');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// First use or corrupt file: start fresh.
|
|
95
|
+
}
|
|
96
|
+
if (!advisories.includes(title))
|
|
97
|
+
advisories.push(title);
|
|
98
|
+
await mkdir(join(repoRoot, '.autoend'), { recursive: true });
|
|
99
|
+
await writeFile(file, JSON.stringify({ advisories }, null, 2));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Dismiss and Suppress edit both stores: the Flow Map (the durable decision)
|
|
103
|
+
* and report.json (so a viewer reload reflects it). Reject needs a Heal to
|
|
104
|
+
* revert, and Heals are not produced yet (ADR-0006 follow-up).
|
|
105
|
+
*/
|
|
106
|
+
async function resolveFinding(artifactDir, repoRoot, id, action, res) {
|
|
107
|
+
if (action === 'reject') {
|
|
108
|
+
// TODO: design the :id semantics for Reject — Heals are not Findings and the UI currently sends heal.flowId — when Heal production lands (ADR-0006 follow-up).
|
|
109
|
+
sendJson(res, 501, { error: 'Reject requires Heals, which are not produced yet (ADR-0006 follow-up)' });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!repoRoot) {
|
|
113
|
+
sendJson(res, 501, { error: 'resolution actions only work where the Flow Map lives — this Report is being viewed away from its repo' });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const artifact = await readRunArtifact(artifactDir);
|
|
117
|
+
const finding = artifact.findings.find((f) => f.id === id);
|
|
118
|
+
if (!finding) {
|
|
119
|
+
sendJson(res, 404, { error: `unknown finding "${id}"` });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (action === 'dismiss') {
|
|
123
|
+
if (finding.kind !== 'regression' || !finding.flowId) {
|
|
124
|
+
sendJson(res, 409, { error: 'Dismiss applies only to Regressions attached to a Flow' });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
await removeFlow(repoRoot, finding.flowId);
|
|
128
|
+
await updateFinding(artifactDir, id, { resolution: 'dismissed' });
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
if (finding.kind !== 'advisory') {
|
|
132
|
+
sendJson(res, 409, { error: 'Suppress applies only to Advisories' });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await suppressTitle(repoRoot, finding.title);
|
|
136
|
+
await updateFinding(artifactDir, id, { resolution: 'suppressed' });
|
|
137
|
+
}
|
|
138
|
+
sendJson(res, 200, { ok: true });
|
|
139
|
+
}
|
|
140
|
+
export function serveReport(artifactDir, port = 0, repoRoot) {
|
|
6
141
|
const server = createServer(async (req, res) => {
|
|
7
142
|
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
8
143
|
try {
|
|
9
|
-
if (req.method === 'GET' && url.pathname === '/') {
|
|
10
|
-
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
11
|
-
res.end(VIEWER_HTML);
|
|
12
|
-
}
|
|
13
|
-
else if (req.method === 'GET' && url.pathname === '/api/report') {
|
|
144
|
+
if (req.method === 'GET' && url.pathname === '/api/report') {
|
|
14
145
|
const report = await readFile(join(artifactDir, 'report.json'));
|
|
15
146
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
16
147
|
res.end(report);
|
|
17
148
|
}
|
|
149
|
+
else if (req.method === 'GET' && url.pathname === '/api/capabilities') {
|
|
150
|
+
sendJson(res, 200, { resolutionActions: Boolean(repoRoot) });
|
|
151
|
+
}
|
|
18
152
|
else if (req.method === 'GET' && url.pathname.startsWith('/evidence/')) {
|
|
19
|
-
const name =
|
|
20
|
-
if (name.startsWith('..') || name.includes('/')) {
|
|
21
|
-
res
|
|
22
|
-
|
|
153
|
+
const name = decodePath(url.pathname.slice('/evidence/'.length));
|
|
154
|
+
if (name === null || name !== normalize(name) || name.startsWith('..') || name.includes('/')) {
|
|
155
|
+
sendStatus(res, 400);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
await sendEvidence(res, join(artifactDir, 'evidence', name), req.headers.range);
|
|
159
|
+
}
|
|
160
|
+
else if (req.method === 'POST') {
|
|
161
|
+
const match = /^\/api\/findings\/([^/]+)\/(dismiss|suppress|reject)$/.exec(url.pathname);
|
|
162
|
+
const id = match && decodePath(match[1]);
|
|
163
|
+
if (!match || id === null) {
|
|
164
|
+
sendStatus(res, 404);
|
|
23
165
|
return;
|
|
24
166
|
}
|
|
25
|
-
|
|
26
|
-
res.writeHead(200, { 'content-type': 'video/webm' });
|
|
27
|
-
res.end(video);
|
|
167
|
+
await resolveFinding(artifactDir, repoRoot, id, match[2], res);
|
|
28
168
|
}
|
|
29
|
-
else if (req.method === '
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
169
|
+
else if (req.method === 'GET') {
|
|
170
|
+
// The built viewer: `/` is index.html (hash routing — no history
|
|
171
|
+
// fallback needed), everything else resolves inside dist/spa.
|
|
172
|
+
const decoded = decodePath(url.pathname === '/' ? 'index.html' : url.pathname.slice(1));
|
|
173
|
+
const rel = decoded === null ? null : normalize(decoded);
|
|
174
|
+
if (rel === null || rel.startsWith('..') || isAbsolute(rel)) {
|
|
175
|
+
sendStatus(res, 400);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
await sendFile(res, join(VIEWER_DIST, rel));
|
|
35
179
|
}
|
|
36
180
|
else {
|
|
37
|
-
res
|
|
38
|
-
res.end();
|
|
181
|
+
sendStatus(res, 404);
|
|
39
182
|
}
|
|
40
183
|
}
|
|
41
184
|
catch {
|
|
42
|
-
res
|
|
43
|
-
res.end();
|
|
185
|
+
sendStatus(res, 500);
|
|
44
186
|
}
|
|
45
187
|
});
|
|
46
188
|
return new Promise((resolve) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/viewer/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/viewer/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bonyadnouri/autoend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Agent-powered end-to-end testing: initiate a Run, agents test your app, review a video-backed Report.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,14 +22,16 @@
|
|
|
22
22
|
"node": ">=23.6"
|
|
23
23
|
},
|
|
24
24
|
"bin": {
|
|
25
|
-
"autoend": "
|
|
25
|
+
"autoend": "dist/cli.js"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"build": "tsc -p tsconfig.json",
|
|
32
|
-
"prepare": "tsc -p tsconfig.json",
|
|
31
|
+
"build": "npm run build:viewer && tsc -p tsconfig.json",
|
|
32
|
+
"prepare": "npm run build:viewer && tsc -p tsconfig.json",
|
|
33
|
+
"build:viewer": "tsc -p viewer/tsconfig.json --noEmit && vite build --config viewer/vite.config.ts",
|
|
34
|
+
"dev:viewer": "vite --config viewer/vite.config.ts",
|
|
33
35
|
"dev": "tsx src/cli.ts",
|
|
34
36
|
"test": "vitest run",
|
|
35
37
|
"typecheck": "tsc --noEmit"
|
|
@@ -43,8 +45,19 @@
|
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@types/node": "^24.0.0",
|
|
48
|
+
"@types/react": "^18.3.12",
|
|
49
|
+
"@types/react-dom": "^18.3.1",
|
|
50
|
+
"@vitejs/plugin-react": "^4.3.3",
|
|
51
|
+
"autoprefixer": "^10.4.20",
|
|
52
|
+
"lucide-react": "^0.454.0",
|
|
53
|
+
"postcss": "^8.4.49",
|
|
54
|
+
"react": "^18.3.1",
|
|
55
|
+
"react-dom": "^18.3.1",
|
|
56
|
+
"react-router-dom": "^6.28.0",
|
|
57
|
+
"tailwindcss": "^3.4.15",
|
|
46
58
|
"tsx": "^4.19.0",
|
|
47
59
|
"typescript": "^5.6.0",
|
|
60
|
+
"vite": "^5.4.11",
|
|
48
61
|
"vitest": "^3.0.0"
|
|
49
62
|
}
|
|
50
63
|
}
|
package/dist/viewer/html.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Report page (ADR-0004: thin viewer over the static Run artifact).
|
|
3
|
-
* Single file, zero external assets — must render offline. Design direction:
|
|
4
|
-
* forensic terminal dossier. The page answers "did anything break?" in the
|
|
5
|
-
* first glance (the verdict), then presents Evidence per tier.
|
|
6
|
-
*/
|
|
7
|
-
export declare const VIEWER_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>autoend Report</title>\n<style>\n :root {\n --bg: #0a0d12;\n --surface: #10141c;\n --line: #1d2432;\n --ink: #dce3ef;\n --dim: #6d7890;\n --hard: #ff5c5c;\n --regression: #f2a53f;\n --heal: #38cfc0;\n --advisory: #8f7ff0;\n --ok: #56d68b;\n --mono: ui-monospace, \"SFMono-Regular\", \"Cascadia Code\", \"JetBrains Mono\", Menlo, Consolas, monospace;\n }\n * { box-sizing: border-box; margin: 0; }\n html { color-scheme: dark; }\n body {\n background:\n radial-gradient(1200px 500px at 50% -10%, #131a26 0%, transparent 60%),\n repeating-linear-gradient(0deg, transparent 0 47px, #ffffff05 47px 48px),\n repeating-linear-gradient(90deg, transparent 0 47px, #ffffff05 47px 48px),\n var(--bg);\n color: var(--ink);\n font-family: var(--mono);\n font-size: 14px;\n line-height: 1.55;\n min-height: 100vh;\n }\n main { max-width: 66rem; margin: 0 auto; padding: 0 1.5rem 6rem; }\n\n header {\n display: flex; justify-content: space-between; align-items: baseline;\n padding: 1.1rem 0; border-bottom: 1px solid var(--line);\n font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--dim);\n }\n header .brand b { color: var(--ink); font-weight: 700; }\n\n .verdict { padding: 3.2rem 0 0; animation: rise .5s ease-out both; }\n .verdict h1 {\n font-size: clamp(2rem, 5.4vw, 3.4rem);\n font-weight: 800; letter-spacing: .01em; line-height: 1.1;\n display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;\n }\n .lamp {\n width: .6em; height: .6em; border-radius: 50%;\n background: var(--verdict-color, var(--ok));\n box-shadow: 0 0 18px var(--verdict-color, var(--ok));\n animation: pulse 2.4s ease-in-out infinite;\n }\n .verdict .sub { margin-top: .9rem; color: var(--dim); font-size: 13px; }\n .verdict .sub b { color: var(--ink); font-weight: 600; }\n\n .stats {\n display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));\n gap: 1px; background: var(--line); border: 1px solid var(--line);\n margin-top: 2.6rem; animation: rise .5s .08s ease-out both;\n }\n .stat { background: var(--surface); padding: .9rem 1rem .8rem; position: relative; }\n .stat::before {\n content: \"\"; position: absolute; inset: 0 auto auto 0; height: 2px; width: 100%;\n background: var(--stat-color, transparent);\n }\n .stat .n { font-size: 1.7rem; font-weight: 800; }\n .stat .l { font-size: 10px; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); }\n .stat.zero .n { color: var(--dim); }\n\n section { margin-top: 3.4rem; animation: rise .5s ease-out both; }\n section:nth-of-type(1) { animation-delay: .14s }\n section:nth-of-type(2) { animation-delay: .20s }\n section:nth-of-type(3) { animation-delay: .26s }\n section:nth-of-type(4) { animation-delay: .32s }\n section h2 {\n font-size: 12px; letter-spacing: .2em; text-transform: uppercase;\n display: flex; align-items: baseline; gap: .7rem;\n }\n section h2::before { content: \"\u25AE\"; color: var(--tier); }\n section h2 .count { color: var(--tier); }\n section .explain { color: var(--dim); font-size: 12.5px; margin: .45rem 0 1.2rem; max-width: 44rem; }\n\n .card {\n border: 1px solid var(--line); border-left: 3px solid var(--tier);\n background: var(--surface); padding: 1.1rem 1.25rem 1.25rem; margin-bottom: 1rem;\n }\n .card h3 { font-size: 15px; font-weight: 700; }\n .card .detail {\n color: var(--dim); font-size: 12.5px; margin-top: .4rem;\n white-space: pre-wrap; word-break: break-word;\n }\n .card video {\n width: 100%; margin-top: 1rem; border: 1px solid var(--line);\n background: #000; display: block;\n }\n .card .flowid { color: var(--dim); font-size: 11px; margin-top: .55rem; letter-spacing: .06em; }\n\n .allclear { text-align: center; padding: 5rem 0 2rem; animation: rise .5s .14s ease-out both; }\n .allclear .mark { font-size: 3rem; color: var(--ok); }\n .allclear h2 { font-size: 1.5rem; letter-spacing: .3em; text-transform: uppercase; margin-top: 1rem; }\n .allclear p { color: var(--dim); margin-top: .8rem; }\n\n footer {\n margin-top: 5rem; padding-top: 1rem; border-top: 1px solid var(--line);\n color: var(--dim); font-size: 11px; letter-spacing: .08em;\n display: flex; justify-content: space-between; flex-wrap: wrap; gap: .5rem;\n }\n\n @keyframes rise { from { opacity: 0; transform: translateY(10px) } to { opacity: 1; transform: none } }\n @keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: .55 } }\n @media (prefers-reduced-motion: reduce) { * { animation: none !important } }\n</style>\n</head>\n<body>\n<main>\n <header>\n <span class=\"brand\"><b>autoend</b> \u2044 run report</span>\n <span id=\"runid\"></span>\n </header>\n <div class=\"verdict\" id=\"verdict\"></div>\n <div class=\"stats\" id=\"stats\"></div>\n <div id=\"body\"></div>\n <footer>\n <span>evidence recorded headless during the run</span>\n <span id=\"stamp\"></span>\n </footer>\n</main>\n<script>\nconst TIERS = {\n 'hard-failure': {\n label: 'Hard failures', color: 'var(--hard)',\n explain: 'Objectively broken \u2014 errors, crashes, failed requests. Fix these first.',\n },\n 'regression': {\n label: 'Regressions', color: 'var(--regression)',\n explain: 'Worked in a previous run, failed in this one. If the removal was intentional, dismiss it to update the baseline.',\n },\n 'heal': {\n label: 'Heals', color: 'var(--heal)',\n explain: 'The UI changed but the goal still works \u2014 the flow script was rewritten. Watch each video to confirm the heal is right.',\n },\n 'advisory': {\n label: 'Advisories', color: 'var(--advisory)',\n explain: 'Agent judgment on UX, accessibility, and speed. Signals worth a look, not verdicts.',\n },\n};\n\nfunction el(tag, attrs, children) {\n const node = document.createElement(tag);\n for (const [k, v] of Object.entries(attrs || {})) {\n if (k === 'style') node.style.cssText = v; else if (k === 'text') node.textContent = v; else node.setAttribute(k, v);\n }\n for (const child of children || []) node.appendChild(child);\n return node;\n}\n\nfetch('/api/report').then(r => r.json()).then(report => {\n const byTier = { 'hard-failure': [], 'regression': [], 'heal': [], 'advisory': [] };\n for (const f of report.findings) (byTier[f.kind] || byTier['advisory']).push(f);\n for (const h of report.heals) byTier['heal'].push({ title: 'Flow \"' + h.flowId + '\" healed', detail: h.summary, flowId: h.flowId, evidence: h.evidence });\n\n const nHard = byTier['hard-failure'].length, nReg = byTier['regression'].length;\n const nHeal = byTier['heal'].length, nAdv = byTier['advisory'].length;\n\n document.getElementById('runid').textContent = report.runId;\n document.getElementById('stamp').textContent = 'effort ' + report.effort + ' \u00B7 ' + report.startedAt;\n\n const durationS = report.finishedAt\n ? ((new Date(report.finishedAt) - new Date(report.startedAt)) / 1000).toFixed(1) + 's'\n : '\u2014';\n\n let text, color;\n if (nHard > 0) { text = nHard + ' hard failure' + (nHard > 1 ? 's' : ''); color = 'var(--hard)'; }\n else if (nReg > 0) { text = nReg + ' regression' + (nReg > 1 ? 's' : ''); color = 'var(--regression)'; }\n else if (nHeal > 0) { text = 'passing, ' + nHeal + ' heal' + (nHeal > 1 ? 's' : '') + ' to verify'; color = 'var(--heal)'; }\n else { text = 'all clear'; color = 'var(--ok)'; }\n\n const verdict = document.getElementById('verdict');\n verdict.style.setProperty('--verdict-color', color);\n verdict.appendChild(el('h1', {}, [el('span', { class: 'lamp' }), el('span', { text })]));\n const sub = el('div', { class: 'sub' });\n sub.innerHTML = 'target <b></b> \u00B7 <b>' + report.flowsReplayed + '</b> flows replayed \u00B7 <b>'\n + report.flowsDiscovered + '</b> discovered \u00B7 <b>' + durationS + '</b>';\n sub.querySelector('b').textContent = report.target;\n verdict.appendChild(sub);\n\n const stats = document.getElementById('stats');\n for (const [kind, tier] of Object.entries(TIERS)) {\n const n = byTier[kind].length;\n stats.appendChild(el('div', { class: 'stat' + (n === 0 ? ' zero' : ''), style: '--stat-color:' + (n > 0 ? tier.color : 'transparent') }, [\n el('div', { class: 'n', text: String(n) }),\n el('div', { class: 'l', text: tier.label }),\n ]));\n }\n\n const body = document.getElementById('body');\n const total = nHard + nReg + nHeal + nAdv;\n if (total === 0) {\n body.appendChild(el('div', { class: 'allclear' }, [\n el('div', { class: 'mark', text: '\u2713' }),\n el('h2', { text: 'all clear' }),\n el('p', { text: report.flowsReplayed > 0\n ? 'Every known flow replayed successfully. Nothing to act on.'\n : 'No flows in the map yet \u2014 the first exploration run will build your baseline.' }),\n ]));\n return;\n }\n\n for (const [kind, tier] of Object.entries(TIERS)) {\n const items = byTier[kind];\n if (items.length === 0) continue;\n const section = el('section', { style: '--tier:' + tier.color });\n section.appendChild(el('h2', {}, [\n el('span', { text: tier.label + ' ' }),\n el('span', { class: 'count', text: String(items.length) }),\n ]));\n section.appendChild(el('div', { class: 'explain', text: tier.explain }));\n for (const f of items) {\n const card = el('div', { class: 'card' }, [el('h3', { text: f.title })]);\n if (f.detail) card.appendChild(el('div', { class: 'detail', text: f.detail }));\n if (f.evidence) card.appendChild(el('video', { controls: '', preload: 'metadata', src: '/evidence/' + f.evidence }));\n if (f.flowId) card.appendChild(el('div', { class: 'flowid', text: 'flow: ' + f.flowId }));\n section.appendChild(card);\n }\n body.appendChild(section);\n }\n});\n</script>\n</body>\n</html>";
|