conductor-remote 1.15.1 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-CbgaQzcX.js +40 -0
- package/dist/assets/index-DLTj56Tj.css +1 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-node/src/autoupdate.js +51 -3
- package/dist-node/src/server.js +46 -21
- package/package.json +1 -1
- package/dist/assets/index-BueoD0ur.js +0 -40
- package/dist/assets/index-Cc0_wQYI.css +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, "SF Pro Text", "Segoe UI", Roboto, sans-serif;--font-mono:ui-monospace, "SF Mono", "JetBrains Mono", "Fira Code", Menlo, monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--radius-3xl:1.5rem;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg:#0a0b0e;--color-surface:#14161b;--color-surface-2:#1b1e26;--color-border:#262a33;--color-border-soft:#1e222a;--color-text:#e7e9ee;--color-muted:#8b909d;--color-faint:#5c616d;--color-accent:#8b7dff;--color-accent-soft:#2a2650;--color-working:#f5a623;--color-idle:#3ecf8e;--color-done:#5b9dff;--color-pr-merged:#ac47ff;--color-pr-draft:#a4a3a2;--color-pr-conflicts:#fd9a00;--color-pr-mergeable:#49de80;--color-add:#3ecf8e;--color-del:#ff6b6b}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}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;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}*{-webkit-tap-highlight-color:transparent}html,body,#root{height:100dvh}html{background:var(--color-bg)}body{background:var(--color-bg);color:var(--color-text);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;overscroll-behavior:none;margin:0;overflow-x:hidden}button{font:inherit;color:inherit;cursor:pointer}::-webkit-scrollbar{width:0;height:0}}@layer components{.card{align-items:center;gap:calc(var(--spacing) * 3);border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface);padding-inline:calc(var(--spacing) * 4);padding-block:calc(var(--spacing) * 3.5);text-align:left;transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));display:flex}.card:active{background-color:var(--color-surface-2);scale:.985}.pill{padding-inline:calc(var(--spacing) * 3.5);padding-block:calc(var(--spacing) * 1.5);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-muted);transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration));border-radius:3.40282e38px}.pill-active{background-color:var(--color-surface-2);color:var(--color-text)}.dot{width:calc(var(--spacing) * 2.5);height:calc(var(--spacing) * 2.5);background-color:var(--color-faint);--pulse-color:var(--color-working);border-radius:3.40282e38px;flex-shrink:0}.dot-pulse{box-shadow:0 0 0 0 var(--pulse-color)}@supports (color:color-mix(in lab,red,red)){.dot-pulse{box-shadow:0 0 color-mix(in srgb,var(--pulse-color) 60%,transparent)}}.dot-pulse{animation:1.6s ease-out infinite pulse}.dot-working{background:var(--color-working);box-shadow:0 0 #f5a62399}@supports (color:color-mix(in lab,red,red)){.dot-working{box-shadow:0 0 color-mix(in srgb,var(--color-working) 60%,transparent)}}.dot-working{animation:1.6s ease-out infinite pulse}.dot-idle{background:var(--color-idle)}.dot-done{background:var(--color-done)}.md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}.md p{margin:.5em 0}.md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:.9em 0 .4em;font-weight:600;line-height:1.3}.md h1{font-size:1.25em}.md h2{font-size:1.15em}.md h3{font-size:1.05em}.md ul,.md ol{margin:.5em 0;padding-left:1.4em}.md ul{list-style:outside}.md ol{list-style:decimal}.md li,.md li>ul,.md li>ol{margin:.2em 0}.md code{background:#1b1e26cc;border-radius:.3rem}@supports (color:color-mix(in lab,red,red)){.md code{background:color-mix(in srgb,var(--color-surface-2) 80%,transparent)}}.md code{font-family:var(--font-mono);padding:.1em .35em;font-size:.85em}.md pre{border:1px solid var(--color-border-soft);background:var(--color-bg);border-radius:.6rem;margin:.5em 0;padding:.6em .75em;overflow-x:auto}.md pre code{background:0 0;padding:0;font-size:12px;line-height:1.5}.md a{color:var(--color-accent);text-underline-offset:2px;text-decoration:underline}.md blockquote{border-left:2px solid var(--color-border);color:var(--color-muted);margin:.5em 0;padding-left:.75em}.md hr{border:0;border-top:1px solid var(--color-border-soft);margin:.75em 0}.md table{border-collapse:collapse;max-width:100%;margin:.5em 0;font-size:.9em;display:block;overflow-x:auto}.md th,.md td{border:1px solid var(--color-border);text-align:left;padding:.3em .6em}.md th{background:var(--color-surface-2);font-weight:600}.md strong{font-weight:600}.md input[type=checkbox]{margin-right:.4em}}@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.inset-y-0{inset-block:0}.top-0{top:0}.top-1\.5{top:calc(var(--spacing) * 1.5)}.top-full{top:100%}.-right-0\.5{right:calc(var(--spacing) * -.5)}.right-1\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.-bottom-0\.5{bottom:calc(var(--spacing) * -.5)}.bottom-0{bottom:0}.left-0{left:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mb-0\.5{margin-bottom:calc(var(--spacing) * .5)}.mb-1{margin-bottom:var(--spacing)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.-ml-1{margin-left:calc(var(--spacing) * -1)}.ml-auto{margin-left:auto}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.size-1\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.size-1\.5\!{width:calc(var(--spacing) * 1.5)!important;height:calc(var(--spacing) * 1.5)!important}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.size-20{width:calc(var(--spacing) * 20);height:calc(var(--spacing) * 20)}.size-60{width:calc(var(--spacing) * 60);height:calc(var(--spacing) * 60)}.size-full{width:100%;height:100%}.h-full{height:100%}.max-h-40{max-height:calc(var(--spacing) * 40)}.w-1{width:var(--spacing)}.w-64{width:calc(var(--spacing) * 64)}.w-\[85\%\]{width:85%}.w-full{width:100%}.max-w-36{max-width:calc(var(--spacing) * 36)}.max-w-80{max-width:calc(var(--spacing) * 80)}.max-w-\[70vw\]{max-width:70vw}.max-w-\[85\%\]{max-width:85%}.max-w-\[92\%\]{max-width:92%}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:0}.min-w-5{min-width:calc(var(--spacing) * 5)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.-rotate-90{rotate:-90deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.resize-none{resize:none}.flex-col{flex-direction:column}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.self-start{align-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-3xl{border-top-left-radius:var(--radius-3xl);border-top-right-radius:var(--radius-3xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-accent\/50{border-color:#8b7dff80}@supports (color:color-mix(in lab,red,red)){.border-accent\/50{border-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.border-border{border-color:var(--color-border)}.border-border-soft{border-color:var(--color-border-soft)}.border-faint{border-color:var(--color-faint)}.border-white\/80{border-color:#fffc}@supports (color:color-mix(in lab,red,red)){.border-white\/80{border-color:color-mix(in oklab,var(--color-white) 80%,transparent)}}.border-t-accent{border-top-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-accent-soft{background-color:var(--color-accent-soft)}.bg-bg{background-color:var(--color-bg)}.bg-bg\/80{background-color:#0a0b0ecc}@supports (color:color-mix(in lab,red,red)){.bg-bg\/80{background-color:color-mix(in oklab,var(--color-bg) 80%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black) 60%,transparent)}}.bg-black\/75{background-color:#000000bf}@supports (color:color-mix(in lab,red,red)){.bg-black\/75{background-color:color-mix(in oklab,var(--color-black) 75%,transparent)}}.bg-del\/10{background-color:#ff6b6b1a}@supports (color:color-mix(in lab,red,red)){.bg-del\/10{background-color:color-mix(in oklab,var(--color-del) 10%,transparent)}}.bg-idle\/10{background-color:#3ecf8e1a}@supports (color:color-mix(in lab,red,red)){.bg-idle\/10{background-color:color-mix(in oklab,var(--color-idle) 10%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-2{background-color:var(--color-surface-2)}.bg-surface\/60{background-color:#14161b99}@supports (color:color-mix(in lab,red,red)){.bg-surface\/60{background-color:color-mix(in oklab,var(--color-surface) 60%,transparent)}}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.bg-working\/10{background-color:#f5a6231a}@supports (color:color-mix(in lab,red,red)){.bg-working\/10{background-color:color-mix(in oklab,var(--color-working) 10%,transparent)}}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-1{padding-inline:var(--spacing)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-3\.5{padding-inline:calc(var(--spacing) * 3.5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-2\.5{padding-bottom:calc(var(--spacing) * 2.5)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11\.5px\]{font-size:11.5px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[14px\]{font-size:14px}.text-\[15px\]{font-size:15px}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.whitespace-pre{white-space:pre}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-add{color:var(--color-add)}.text-del{color:var(--color-del)}.text-faint{color:var(--color-faint)}.text-idle{color:var(--color-idle)}.text-muted{color:var(--color-muted)}.text-text{color:var(--color-text)}.text-white{color:var(--color-white)}.text-working{color:var(--color-working)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.underline-offset-2{text-underline-offset:2px}.\[color-scheme\:dark\]{color-scheme:dark}.opacity-60{opacity:.6}.shadow-\[0_0_0_100vmax_rgba\(0\,0\,0\,0\.5\)\]{--tw-shadow:0 0 0 100vmax var(--tw-shadow-color,#00000080);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-surface{--tw-ring-color:var(--color-surface)}.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,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:font-sans::placeholder{font-family:var(--font-sans)}.placeholder\:text-faint::placeholder{color:var(--color-faint)}.focus-within\:border-accent\/60:focus-within{border-color:#8b7dff99}@supports (color:color-mix(in lab,red,red)){.focus-within\:border-accent\/60:focus-within{border-color:color-mix(in oklab,var(--color-accent) 60%,transparent)}}@media(hover:hover){.hover\:underline:hover{text-decoration-line:underline}}.active\:scale-90:active{--tw-scale-x:90%;--tw-scale-y:90%;--tw-scale-z:90%;scale:var(--tw-scale-x) var(--tw-scale-y)}.active\:bg-black\/70:active{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.active\:bg-black\/70:active{background-color:color-mix(in oklab,var(--color-black) 70%,transparent)}}.active\:bg-surface:active{background-color:var(--color-surface)}.active\:bg-surface-2:active,.disabled\:bg-surface-2:disabled{background-color:var(--color-surface-2)}.disabled\:text-faint:disabled{color:var(--color-faint)}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:48rem){.md\:static{position:static}.md\:inset-0{inset:0}.md\:z-auto{z-index:auto}.md\:m-auto{margin:auto}.md\:block{display:block}.md\:hidden{display:none}.md\:h-fit{height:fit-content}.md\:w-72{width:calc(var(--spacing) * 72)}.md\:max-w-none{max-width:none}.md\:shrink-0{flex-shrink:0}.md\:translate-x-0{--tw-translate-x:0;translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:rounded-3xl{border-radius:var(--radius-3xl)}.md\:transition-none{transition-property:none}}@media(min-width:64rem){.lg\:static{position:static}.lg\:z-auto{z-index:auto}.lg\:w-80{width:calc(var(--spacing) * 80)}.lg\:w-\[380px\]{width:380px}.lg\:shrink-0{flex-shrink:0}.lg\:border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.lg\:border-border-soft{border-color:var(--color-border-soft)}}@media(min-width:80rem){.xl\:w-\[460px\]{width:460px}}}@keyframes pulse{50%{opacity:.5}}@keyframes fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}.fade-in{animation:.18s ease-out fade-in}.pt-safe{padding-top:max(env(safe-area-inset-top),.5rem)}.pb-safe{padding-bottom:max(env(safe-area-inset-bottom),.5rem)}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}
|
package/dist/index.html
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
12
12
|
<link rel="icon" type="image/svg+xml" href="/icon.svg" />
|
|
13
13
|
<title>Conductor Remote</title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-CbgaQzcX.js"></script>
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DLTj56Tj.css">
|
|
16
16
|
<link rel="manifest" href="/manifest.webmanifest"></head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(i[r])return;let
|
|
1
|
+
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,o)=>{const r=e||("document"in self?document.currentScript.src:"")||location.href;if(i[r])return;let t={};const l=e=>n(e,r),c={module:{uri:r},exports:t,require:l};i[r]=Promise.all(s.map(e=>c[e]||l(e))).then(e=>(o(...e),t))}}define(["./workbox-9c191d2f"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"index.html",revision:"b74d3538a28fb5da75898594318ad00a"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-DLTj56Tj.css",revision:null},{url:"assets/index-CbgaQzcX.js",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
* KeepAlive restarts it into the freshly-installed code (the plist's baked `bin/cli.js` path is stable
|
|
9
9
|
* across a global reinstall, so no re-`service install` is needed).
|
|
10
10
|
*
|
|
11
|
+
* Beyond that 6h registry poll, a cheap local check reconciles a disk↔process version skew: if the
|
|
12
|
+
* installed package.json on disk no longer matches the version this process booted as — an out-of-band
|
|
13
|
+
* `npm i -g`/deploy swapped the global install underneath us — it exit()→restarts to load it. Without
|
|
14
|
+
* this, a stale backend keeps serving the newer frontend from disk (the API contract drifts, e.g. the
|
|
15
|
+
* `icon` field vanishes and every repo avatar falls to a monogram) until the next npm poll happens to
|
|
16
|
+
* find a version newer than the *running* one — which it never will if disk was bumped out-of-band.
|
|
17
|
+
*
|
|
11
18
|
* Two hard gates keep this from firing where it shouldn't:
|
|
12
19
|
* - CONDUCTOR_REMOTE_MANAGED=1 — set only by `service install` in the plist, so it proves we are the
|
|
13
20
|
* launchd-managed daemon and that exit()→KeepAlive-restart is a safe way to reload.
|
|
@@ -30,6 +37,9 @@ const projectDir = packageRoot(import.meta.dirname);
|
|
|
30
37
|
const REGISTRY = process.env.NPM_REGISTRY ?? 'https://registry.npmjs.org';
|
|
31
38
|
const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
32
39
|
const FIRST_DELAY_MS = 90 * 1000;
|
|
40
|
+
// A local package.json read is free next to a network poll, so reconcile disk↔process skew often —
|
|
41
|
+
// this heals an out-of-band upgrade in ~a minute instead of waiting up to 6h for the registry poll.
|
|
42
|
+
const DISK_CHECK_INTERVAL_MS = 60 * 1000;
|
|
33
43
|
function readVersion() {
|
|
34
44
|
try {
|
|
35
45
|
const pkg = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf8'));
|
|
@@ -40,6 +50,20 @@ function readVersion() {
|
|
|
40
50
|
}
|
|
41
51
|
}
|
|
42
52
|
const CURRENT = readVersion();
|
|
53
|
+
/**
|
|
54
|
+
* Fresh read of the installed package.json version — what's on disk *now*, which diverges from the
|
|
55
|
+
* frozen `CURRENT` once an out-of-band install swaps the global package. Null if unreadable or
|
|
56
|
+
* unparseable, so a transient read failure can never be mistaken for a skew (and loop-restart us).
|
|
57
|
+
*/
|
|
58
|
+
function diskVersion() {
|
|
59
|
+
try {
|
|
60
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf8'));
|
|
61
|
+
return pkg.version && parseVersion(pkg.version) ? pkg.version : null;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
43
67
|
const status = {
|
|
44
68
|
current: CURRENT,
|
|
45
69
|
latest: null,
|
|
@@ -117,6 +141,28 @@ function resolveMode() {
|
|
|
117
141
|
return canAuto ? 'auto' : 'check';
|
|
118
142
|
return canAuto ? 'auto' : 'off';
|
|
119
143
|
}
|
|
144
|
+
let restarting = false;
|
|
145
|
+
/** Schedule an exit so launchd's KeepAlive respawns us into the on-disk code. Idempotent per process. */
|
|
146
|
+
function scheduleRestart(reason) {
|
|
147
|
+
if (restarting)
|
|
148
|
+
return;
|
|
149
|
+
restarting = true;
|
|
150
|
+
log(`${reason}; restarting to apply (launchd KeepAlive brings the relay back).`);
|
|
151
|
+
// Let the log line flush, then exit; KeepAlive respawns us into the new code.
|
|
152
|
+
setTimeout(() => process.exit(0), 500).unref();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Reload if the on-disk install no longer matches this running process — an out-of-band upgrade
|
|
156
|
+
* (manual `npm i -g`, `yarn deploy`, or CI) that the registry poll won't catch, since it compares
|
|
157
|
+
* npm's latest against our *running* version, not against disk. Any mismatch converges process→disk;
|
|
158
|
+
* a null read (unreadable/unparseable) is ignored so we never restart-loop on a transient failure.
|
|
159
|
+
*/
|
|
160
|
+
function restartIfDiskSkewed() {
|
|
161
|
+
const onDisk = diskVersion();
|
|
162
|
+
if (!onDisk || onDisk === CURRENT)
|
|
163
|
+
return;
|
|
164
|
+
scheduleRestart(`on-disk ${onDisk} ≠ running ${CURRENT} (out-of-band install)`);
|
|
165
|
+
}
|
|
120
166
|
let inFlight = false;
|
|
121
167
|
async function runCheck(mode) {
|
|
122
168
|
if (inFlight)
|
|
@@ -136,9 +182,7 @@ async function runCheck(mode) {
|
|
|
136
182
|
}
|
|
137
183
|
log(`updating ${CURRENT} → ${latest} via \`npm i -g ${NAME}@latest\`…`);
|
|
138
184
|
await installLatest();
|
|
139
|
-
|
|
140
|
-
// Let the log line flush, then exit; KeepAlive respawns us into the new code.
|
|
141
|
-
setTimeout(() => process.exit(0), 500).unref();
|
|
185
|
+
scheduleRestart(`installed ${latest}`);
|
|
142
186
|
}
|
|
143
187
|
catch (err) {
|
|
144
188
|
status.lastError = err instanceof Error ? err.message : String(err);
|
|
@@ -157,4 +201,8 @@ export function startAutoUpdate() {
|
|
|
157
201
|
log(`enabled (mode=${mode}, current=${CURRENT}); first check in ${FIRST_DELAY_MS / 1000}s, then every 6h.`);
|
|
158
202
|
setTimeout(() => void runCheck(mode), FIRST_DELAY_MS).unref();
|
|
159
203
|
setInterval(() => void runCheck(mode), CHECK_INTERVAL_MS).unref();
|
|
204
|
+
// Only `auto` may exit()→restart (KeepAlive is guaranteed there); `check` reports but never acts,
|
|
205
|
+
// so the disk-skew heal — which reloads by exiting — is gated to `auto` alongside the npm install.
|
|
206
|
+
if (mode === 'auto')
|
|
207
|
+
setInterval(restartIfDiskSkewed, DISK_CHECK_INTERVAL_MS).unref();
|
|
160
208
|
}
|
package/dist-node/src/server.js
CHANGED
|
@@ -2,6 +2,7 @@ import crypto from 'node:crypto';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import http from 'node:http';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import zlib from 'node:zlib';
|
|
5
6
|
import { startAutoUpdate, updateStatus } from "./autoupdate.js";
|
|
6
7
|
import { loadConfig } from "./config.js";
|
|
7
8
|
import { ConductorDb } from "./db.js";
|
|
@@ -22,10 +23,34 @@ const MIME = {
|
|
|
22
23
|
'.svg': 'image/svg+xml',
|
|
23
24
|
'.png': 'image/png'
|
|
24
25
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Successful GETs are conditional + compressed to keep the phone's polling cheap.
|
|
28
|
+
* `no-cache` (not `no-store`) means the browser must revalidate on every tick —
|
|
29
|
+
* the relay still runs the handler and auth each time, so data is never stale;
|
|
30
|
+
* a matching ETag just elides the redundant body (304), and changed bodies over
|
|
31
|
+
* ~1 KB go out gzipped. Errors and non-GETs stay unconditional `no-store`.
|
|
32
|
+
*/
|
|
33
|
+
function json(req, res, status, body) {
|
|
34
|
+
const payload = Buffer.from(JSON.stringify(body));
|
|
35
|
+
if (status !== 200 || req.method !== 'GET') {
|
|
36
|
+
res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
|
|
37
|
+
return void res.end(payload);
|
|
38
|
+
}
|
|
39
|
+
// Weak: the same entity may be delivered gzipped or plain.
|
|
40
|
+
const etag = `W/"${crypto.createHash('sha1').update(payload).digest('base64url')}"`;
|
|
41
|
+
const headers = {
|
|
42
|
+
'content-type': 'application/json; charset=utf-8',
|
|
43
|
+
'cache-control': 'no-cache',
|
|
44
|
+
etag,
|
|
45
|
+
vary: 'accept-encoding'
|
|
46
|
+
};
|
|
47
|
+
if (req.headers['if-none-match'] === etag)
|
|
48
|
+
return void res.writeHead(304, headers).end();
|
|
49
|
+
if (payload.length > 1024 && /\bgzip\b/.test(String(req.headers['accept-encoding'] ?? ''))) {
|
|
50
|
+
headers['content-encoding'] = 'gzip';
|
|
51
|
+
return void res.writeHead(200, headers).end(zlib.gzipSync(payload));
|
|
52
|
+
}
|
|
53
|
+
res.writeHead(200, headers).end(payload);
|
|
29
54
|
}
|
|
30
55
|
/** Constant-time string compare — the token is the sole internet-facing gate when exposed via Funnel. */
|
|
31
56
|
function tokenEq(candidate) {
|
|
@@ -85,14 +110,14 @@ const server = http.createServer(async (req, res) => {
|
|
|
85
110
|
return serveStatic(req, res, pathname);
|
|
86
111
|
// Everything under /api requires the shared secret.
|
|
87
112
|
if (!authed(req))
|
|
88
|
-
return json(res, 401, { error: 'unauthorized' });
|
|
113
|
+
return json(req, res, 401, { error: 'unauthorized' });
|
|
89
114
|
try {
|
|
90
115
|
// GET /api/state — workspace list with active-session status
|
|
91
116
|
if (req.method === 'GET' && pathname === '/api/state') {
|
|
92
117
|
const update = updateStatus();
|
|
93
118
|
const workspaces = reads.listWorkspaces();
|
|
94
119
|
attachPrStatus(workspaces); // colours pr_status from cache; refreshes stale entries in the background
|
|
95
|
-
return json(res, 200, {
|
|
120
|
+
return json(req, res, 200, {
|
|
96
121
|
workspaces,
|
|
97
122
|
actuator: await describeActuator(actuator),
|
|
98
123
|
version: update.current,
|
|
@@ -104,10 +129,10 @@ const server = http.createServer(async (req, res) => {
|
|
|
104
129
|
if (req.method === 'GET' && m) {
|
|
105
130
|
const icon = reads.resolveRepoIcon(decodeURIComponent(m[1]));
|
|
106
131
|
if (!icon)
|
|
107
|
-
return json(res, 404, { error: 'no icon' });
|
|
132
|
+
return json(req, res, 404, { error: 'no icon' });
|
|
108
133
|
return void fs.readFile(icon.path, (err, data) => {
|
|
109
134
|
if (err)
|
|
110
|
-
return void json(res, 404, { error: 'no icon' });
|
|
135
|
+
return void json(req, res, 404, { error: 'no icon' });
|
|
111
136
|
// Cache briefly on the phone; the resolver itself refreshes within ~30s of an icon change.
|
|
112
137
|
res.writeHead(200, { 'content-type': icon.contentType, 'cache-control': 'public, max-age=300' });
|
|
113
138
|
res.end(data);
|
|
@@ -116,42 +141,42 @@ const server = http.createServer(async (req, res) => {
|
|
|
116
141
|
// GET /api/workspaces/:id/sessions
|
|
117
142
|
m = pathname.match(/^\/api\/workspaces\/([^/]+)\/sessions$/);
|
|
118
143
|
if (req.method === 'GET' && m) {
|
|
119
|
-
return json(res, 200, { sessions: reads.listSessions(decodeURIComponent(m[1])) });
|
|
144
|
+
return json(req, res, 200, { sessions: reads.listSessions(decodeURIComponent(m[1])) });
|
|
120
145
|
}
|
|
121
146
|
// POST /api/workspaces/:id/sessions — open a new chat (Cmd+T) in the workspace
|
|
122
147
|
if (req.method === 'POST' && m) {
|
|
123
148
|
const workspaceId = decodeURIComponent(m[1]);
|
|
124
149
|
const ws = reads.getWorkspace(workspaceId);
|
|
125
150
|
if (!ws)
|
|
126
|
-
return json(res, 404, { error: 'workspace not found' });
|
|
151
|
+
return json(req, res, 404, { error: 'workspace not found' });
|
|
127
152
|
const before = new Set(reads.listSessions(workspaceId).map(s => s.id));
|
|
128
153
|
const result = await newChat(ws);
|
|
129
154
|
if (!result.ok)
|
|
130
|
-
return json(res, 502, result);
|
|
155
|
+
return json(req, res, 502, result);
|
|
131
156
|
// The new session lands in the DB a beat after Cmd+T — poll for the fresh id.
|
|
132
157
|
let sessionId = null;
|
|
133
158
|
for (let i = 0; i < 12 && !sessionId; i++) {
|
|
134
159
|
await new Promise(r => setTimeout(r, 500));
|
|
135
160
|
sessionId = reads.listSessions(workspaceId).find(s => !before.has(s.id))?.id ?? null;
|
|
136
161
|
}
|
|
137
|
-
return json(res, 200, { ok: true, sessionId });
|
|
162
|
+
return json(req, res, 200, { ok: true, sessionId });
|
|
138
163
|
}
|
|
139
164
|
// GET /api/workspaces/:id/diff
|
|
140
165
|
m = pathname.match(/^\/api\/workspaces\/([^/]+)\/diff$/);
|
|
141
166
|
if (req.method === 'GET' && m) {
|
|
142
167
|
const ws = reads.getWorkspace(decodeURIComponent(m[1]));
|
|
143
168
|
if (!ws)
|
|
144
|
-
return json(res, 404, { error: 'workspace not found' });
|
|
169
|
+
return json(req, res, 404, { error: 'workspace not found' });
|
|
145
170
|
if (!ws.worktree)
|
|
146
|
-
return json(res, 409, { error: 'worktree path unresolved' });
|
|
171
|
+
return json(req, res, 409, { error: 'worktree path unresolved' });
|
|
147
172
|
const diff = await workspaceDiff(ws.worktree, ws.baseBranch);
|
|
148
|
-
return json(res, 200, diff);
|
|
173
|
+
return json(req, res, 200, diff);
|
|
149
174
|
}
|
|
150
175
|
// GET /api/sessions/:id/messages?after=<rowid>
|
|
151
176
|
m = pathname.match(/^\/api\/sessions\/([^/]+)\/messages$/);
|
|
152
177
|
if (req.method === 'GET' && m) {
|
|
153
178
|
const after = Number(url.searchParams.get('after') ?? 0);
|
|
154
|
-
return json(res, 200, reads.getMessages(decodeURIComponent(m[1]), Number.isFinite(after) ? after : 0));
|
|
179
|
+
return json(req, res, 200, reads.getMessages(decodeURIComponent(m[1]), Number.isFinite(after) ? after : 0));
|
|
155
180
|
}
|
|
156
181
|
// POST /api/sessions/:id/prompt { text }
|
|
157
182
|
m = pathname.match(/^\/api\/sessions\/([^/]+)\/prompt$/);
|
|
@@ -160,19 +185,19 @@ const server = http.createServer(async (req, res) => {
|
|
|
160
185
|
const body = JSON.parse((await readBody(req)) || '{}');
|
|
161
186
|
const text = (body.text ?? '').trim();
|
|
162
187
|
if (!text)
|
|
163
|
-
return json(res, 400, { error: 'empty prompt' });
|
|
188
|
+
return json(req, res, 400, { error: 'empty prompt' });
|
|
164
189
|
const ws = body.workspaceId
|
|
165
190
|
? reads.getWorkspace(body.workspaceId)
|
|
166
191
|
: (reads.listWorkspaces().find(w => w.active_session_id === sessionId) ?? null);
|
|
167
192
|
if (!ws)
|
|
168
|
-
return json(res, 404, { error: 'workspace for session not found' });
|
|
193
|
+
return json(req, res, 404, { error: 'workspace for session not found' });
|
|
169
194
|
const result = await actuator.send({ workspace: ws, sessionId }, text);
|
|
170
|
-
return json(res, result.ok ? 200 : 502, result);
|
|
195
|
+
return json(req, res, result.ok ? 200 : 502, result);
|
|
171
196
|
}
|
|
172
|
-
return json(res, 404, { error: 'no route', pathname });
|
|
197
|
+
return json(req, res, 404, { error: 'no route', pathname });
|
|
173
198
|
}
|
|
174
199
|
catch (err) {
|
|
175
|
-
return json(res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
200
|
+
return json(req, res, 500, { error: err instanceof Error ? err.message : String(err) });
|
|
176
201
|
}
|
|
177
202
|
});
|
|
178
203
|
server.listen(cfg.port, cfg.host, () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "yarn@4.15.0",
|
|
6
6
|
"description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",
|