quadwork 1.17.3 → 1.18.1
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/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +3 -3
- package/out/__next._full.txt +14 -14
- package/out/__next._head.txt +4 -4
- package/out/__next._index.txt +8 -8
- package/out/__next._tree.txt +2 -2
- package/out/_next/static/chunks/0ff8u_cv_ch08.js +3 -0
- package/out/_next/static/chunks/{0ewow-yz7dvp~.js → 0p70ihm9n4drm.js} +1 -1
- package/out/_next/static/chunks/{12ins7yi1imrc.css → 0tv48-.848bk7.css} +1 -1
- package/out/_next/static/chunks/{0sgw2ao.8pb6z.js → 0xwbrozjfr9d5.js} +7 -7
- package/out/_next/static/chunks/13fv-yi7.v52g.js +1 -0
- package/out/_not-found/__next._full.txt +13 -13
- package/out/_not-found/__next._head.txt +4 -4
- package/out/_not-found/__next._index.txt +8 -8
- package/out/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/out/_not-found/__next._not-found.txt +3 -3
- package/out/_not-found/__next._tree.txt +2 -2
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +13 -13
- package/out/app-shell/__next._full.txt +13 -13
- package/out/app-shell/__next._head.txt +4 -4
- package/out/app-shell/__next._index.txt +8 -8
- package/out/app-shell/__next._tree.txt +2 -2
- package/out/app-shell/__next.app-shell.__PAGE__.txt +2 -2
- package/out/app-shell/__next.app-shell.txt +3 -3
- package/out/app-shell.html +1 -1
- package/out/app-shell.txt +13 -13
- package/out/index.html +1 -1
- package/out/index.txt +14 -14
- package/out/project/_/__next._full.txt +14 -14
- package/out/project/_/__next._head.txt +4 -4
- package/out/project/_/__next._index.txt +8 -8
- package/out/project/_/__next._tree.txt +2 -2
- package/out/project/_/__next.project.$d$id.__PAGE__.txt +3 -3
- package/out/project/_/__next.project.$d$id.txt +3 -3
- package/out/project/_/__next.project.txt +3 -3
- package/out/project/_/queue/__next._full.txt +14 -14
- package/out/project/_/queue/__next._head.txt +4 -4
- package/out/project/_/queue/__next._index.txt +8 -8
- package/out/project/_/queue/__next._tree.txt +2 -2
- package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +3 -3
- package/out/project/_/queue/__next.project.$d$id.queue.txt +3 -3
- package/out/project/_/queue/__next.project.$d$id.txt +3 -3
- package/out/project/_/queue/__next.project.txt +3 -3
- package/out/project/_/queue.html +1 -1
- package/out/project/_/queue.txt +14 -14
- package/out/project/_.html +1 -1
- package/out/project/_.txt +14 -14
- package/out/settings/__next._full.txt +14 -14
- package/out/settings/__next._head.txt +4 -4
- package/out/settings/__next._index.txt +8 -8
- package/out/settings/__next._tree.txt +2 -2
- package/out/settings/__next.settings.__PAGE__.txt +3 -3
- package/out/settings/__next.settings.txt +3 -3
- package/out/settings.html +1 -1
- package/out/settings.txt +14 -14
- package/out/setup/__next._full.txt +14 -14
- package/out/setup/__next._head.txt +4 -4
- package/out/setup/__next._index.txt +8 -8
- package/out/setup/__next._tree.txt +2 -2
- package/out/setup/__next.setup.__PAGE__.txt +3 -3
- package/out/setup/__next.setup.txt +3 -3
- package/out/setup.html +1 -1
- package/out/setup.txt +14 -14
- package/package.json +1 -1
- package/server/index.js +180 -108
- package/server/routes.js +14 -0
- package/out/_next/static/chunks/054b72jq_7mig.js +0 -3
- package/out/_next/static/chunks/0m48m-8no_mew.js +0 -1
- /package/out/_next/static/{VL6j_Q-L6f7tGZD4854DN → WDRw2uNz8RE4CDa5ntj1v}/_buildManifest.js +0 -0
- /package/out/_next/static/{VL6j_Q-L6f7tGZD4854DN → WDRw2uNz8RE4CDa5ntj1v}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{VL6j_Q-L6f7tGZD4854DN → WDRw2uNz8RE4CDa5ntj1v}/_ssgManifest.js +0 -0
package/out/setup.html
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" class="geist_mono_8d43a2aa-module__8Li5zG__variable h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/chunks/12ins7yi1imrc.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0ze4gu236oq96.js"/><script src="/_next/static/chunks/0.bbxho1vnxin.js" async=""></script><script src="/_next/static/chunks/0n~dq4kpx9xxx.js" async=""></script><script src="/_next/static/chunks/0zfotsowwll1x.js" async=""></script><script src="/_next/static/chunks/0pqt~8bl3ukh4.js" async=""></script><script src="/_next/static/chunks/turbopack-0qm-e3ifrz~2u.js" async=""></script><script src="/_next/static/chunks/0m48m-8no_mew.js" async=""></script><script src="/_next/static/chunks/0d3shmwh5_nmn.js" async=""></script><script src="/_next/static/chunks/0uz5svjlo9dwl.js" async=""></script><meta name="next-size-adjust" content=""/><title>QuadWork</title><meta name="description" content="Unified dashboard for multi-agent coding teams"/><link rel="icon" href="/favicon.ico?favicon.05o2q2p4kvnq_.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex flex-col"><div hidden=""><!--$--><!--/$--></div><header class="sticky top-0 z-40 flex h-12 items-center justify-between border-b border-white/10 bg-neutral-950/90 px-4 backdrop-blur" aria-hidden="true"></header><div class="flex flex-1 min-h-0"><button type="button" class="fixed top-14 left-2 z-30 lg:hidden w-10 h-10 flex items-center justify-center bg-bg-surface border border-border text-text-muted hover:text-accent"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M3 5h14M3 10h14M3 15h14"></path></svg></button><aside class="fixed inset-y-0 left-0 z-50 w-52 bg-bg-surface border-r border-border flex flex-col py-3 px-2 items-stretch overflow-y-auto transition-transform duration-200 ease-in-out lg:hidden -translate-x-full"><button type="button" class="self-end shrink-0 w-10 h-10 flex items-center justify-center text-text-muted hover:text-accent mb-1"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M5 5l10 10M15 5L5 15"></path></svg></button><a class="flex items-center gap-2 rounded-sm transition-colors px-2 py-2 text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg><span class="text-xs">Home</span></a><div class="h-px bg-border my-2 "></div><div class="flex-1 flex flex-col gap-2 overflow-y-auto min-h-0 "><a class="flex items-center gap-2 rounded-full transition-colors px-2 py-2 border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] rounded-sm" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg><span class="text-xs text-text-muted">New Project</span></a></div><div class="h-px bg-border my-2 "></div><a class="flex items-center gap-2 rounded-sm transition-colors px-2 py-2 text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg><span class="text-xs">Settings</span></a></aside><aside class="hidden lg:flex shrink-0 h-full border-r border-border bg-bg-surface flex-col py-3 transition-[width] duration-200 ease-in-out overflow-hidden w-16 items-center"><a class="flex items-center gap-2 rounded-sm transition-colors w-10 h-10 justify-center self-center text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="h-px bg-border my-2 w-6 self-center"></div><div class="flex-1 flex flex-col gap-2 overflow-y-auto min-h-0 items-center"><a class="flex items-center gap-2 rounded-full transition-colors w-10 h-10 justify-center border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="h-px bg-border my-2 w-6 self-center"></div><a class="flex items-center gap-2 rounded-sm transition-colors w-10 h-10 justify-center self-center text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a><div class="h-1"></div><button class="flex shrink-0 items-center justify-center w-10 h-10 rounded-sm border border-border text-text-muted hover:text-accent hover:border-accent/50 transition-colors self-center" title="Expand sidebar"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"></path></svg></button></aside><main class="flex-1 min-w-0 overflow-auto"><div class="h-full overflow-y-auto"></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/0ze4gu236oq96.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[52368,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"LocaleProvider\"]\n3:I[43688,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n4:I[26704,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n5:I[22140,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n6:I[39756,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n7:I[37457,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n8:I[94810,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\",\"/_next/static/chunks/0uz5svjlo9dwl.js\"],\"default\"]\n9:I[97367,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"OutletBoundary\"]\na:\"$Sreact.suspense\"\nd:I[97367,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"ViewportBoundary\"]\nf:I[97367,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"MetadataBoundary\"]\n11:I[68027,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/12ins7yi1imrc.css\",\"style\"]\n:HL[\"/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"setup\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"setup\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/12ins7yi1imrc.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0m48m-8no_mew.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0d3shmwh5_nmn.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_mono_8d43a2aa-module__8Li5zG__variable h-full\",\"children\":[\"$\",\"body\",null,{\"className\":\"h-full flex flex-col\",\"children\":[\"$\",\"$L2\",null,{\"children\":[[\"$\",\"$L3\",null,{}],[\"$\",\"$L4\",null,{}],[\"$\",\"div\",null,{\"className\":\"flex flex-1 min-h-0\",\"children\":[[\"$\",\"$L5\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L8\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0uz5svjlo9dwl.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$a\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@b\"}]}]]}],{},null,false,null]},null,false,\"$@c\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$Ld\",null,{\"children\":\"$Le\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lf\",null,{\"children\":[\"$\",\"$a\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L10\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$11\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/12ins7yi1imrc.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"VL6j_Q-L6f7tGZD4854DN\"}\n"])</script><script>self.__next_f.push([1,"12:[]\nc:\"$W12\"\n"])</script><script>self.__next_f.push([1,"e:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"13:I[27201,[\"/_next/static/chunks/0m48m-8no_mew.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"IconMark\"]\nb:null\n10:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.05o2q2p4kvnq_.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L13\",\"3\",{}]]\n"])</script></body></html>
|
|
1
|
+
<!DOCTYPE html><html lang="en" class="geist_mono_8d43a2aa-module__8Li5zG__variable h-full"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/chunks/0tv48-.848bk7.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0ze4gu236oq96.js"/><script src="/_next/static/chunks/0.bbxho1vnxin.js" async=""></script><script src="/_next/static/chunks/0n~dq4kpx9xxx.js" async=""></script><script src="/_next/static/chunks/0zfotsowwll1x.js" async=""></script><script src="/_next/static/chunks/0pqt~8bl3ukh4.js" async=""></script><script src="/_next/static/chunks/turbopack-0qm-e3ifrz~2u.js" async=""></script><script src="/_next/static/chunks/13fv-yi7.v52g.js" async=""></script><script src="/_next/static/chunks/0d3shmwh5_nmn.js" async=""></script><script src="/_next/static/chunks/0uz5svjlo9dwl.js" async=""></script><meta name="next-size-adjust" content=""/><title>QuadWork</title><meta name="description" content="Unified dashboard for multi-agent coding teams"/><link rel="icon" href="/favicon.ico?favicon.05o2q2p4kvnq_.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex flex-col"><div hidden=""><!--$--><!--/$--></div><header class="sticky top-0 z-40 flex h-12 items-center justify-between border-b border-white/10 bg-neutral-950/90 px-4 backdrop-blur" aria-hidden="true"></header><div class="flex flex-1 min-h-0"><button type="button" class="fixed top-14 left-2 z-30 lg:hidden w-10 h-10 flex items-center justify-center bg-bg-surface border border-border text-text-muted hover:text-accent"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M3 5h14M3 10h14M3 15h14"></path></svg></button><aside class="fixed inset-y-0 left-0 z-50 w-52 bg-bg-surface border-r border-border flex flex-col py-3 px-2 items-stretch overflow-y-auto transition-transform duration-200 ease-in-out lg:hidden -translate-x-full"><button type="button" class="self-end shrink-0 w-10 h-10 flex items-center justify-center text-text-muted hover:text-accent mb-1"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M5 5l10 10M15 5L5 15"></path></svg></button><a class="flex items-center gap-2 rounded-sm transition-colors px-2 py-2 text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg><span class="text-xs">Home</span></a><div class="h-px bg-border my-2 "></div><div class="flex-1 flex flex-col gap-2 overflow-y-auto min-h-0 "><a class="flex items-center gap-2 rounded-full transition-colors px-2 py-2 border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] rounded-sm" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg><span class="text-xs text-text-muted">New Project</span></a></div><div class="h-px bg-border my-2 "></div><a class="flex items-center gap-2 rounded-sm transition-colors px-2 py-2 text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg><span class="text-xs">Settings</span></a></aside><aside class="hidden lg:flex shrink-0 h-full border-r border-border bg-bg-surface flex-col py-3 transition-[width] duration-200 ease-in-out overflow-hidden w-16 items-center"><a class="flex items-center gap-2 rounded-sm transition-colors w-10 h-10 justify-center self-center text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Home" href="/"><svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10L10 3l7 7"></path><path d="M5 8.5V16h3.5v-4h3v4H15V8.5"></path></svg></a><div class="h-px bg-border my-2 w-6 self-center"></div><div class="flex-1 flex flex-col gap-2 overflow-y-auto min-h-0 items-center"><a class="flex items-center gap-2 rounded-full transition-colors w-10 h-10 justify-center border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Add project" href="/setup"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M8 3v10M3 8h10"></path></svg></a></div><div class="h-px bg-border my-2 w-6 self-center"></div><a class="flex items-center gap-2 rounded-sm transition-colors w-10 h-10 justify-center self-center text-text-muted hover:text-text hover:bg-[#1a1a1a]" title="Settings" href="/settings"><svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="9" r="2.5"></circle><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 011.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 010 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 01-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 01-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 010-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 011.3-.7z"></path></svg></a><div class="h-1"></div><button class="flex shrink-0 items-center justify-center w-10 h-10 rounded-sm border border-border text-text-muted hover:text-accent hover:border-accent/50 transition-colors self-center" title="Expand sidebar"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"></path></svg></button></aside><main class="flex-1 min-w-0 overflow-auto"><div class="h-full overflow-y-auto"></div><!--$--><!--/$--></main></div><script src="/_next/static/chunks/0ze4gu236oq96.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[52368,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"LocaleProvider\"]\n3:I[43688,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n4:I[26704,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n5:I[22140,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n6:I[39756,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n7:I[37457,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\"]\n8:I[94810,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\",\"/_next/static/chunks/0uz5svjlo9dwl.js\"],\"default\"]\n9:I[97367,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"OutletBoundary\"]\na:\"$Sreact.suspense\"\nd:I[97367,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"ViewportBoundary\"]\nf:I[97367,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"MetadataBoundary\"]\n11:I[68027,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/0tv48-.848bk7.css\",\"style\"]\n:HL[\"/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"setup\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"setup\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",16],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0tv48-.848bk7.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/13fv-yi7.v52g.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0d3shmwh5_nmn.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_mono_8d43a2aa-module__8Li5zG__variable h-full\",\"children\":[\"$\",\"body\",null,{\"className\":\"h-full flex flex-col\",\"children\":[\"$\",\"$L2\",null,{\"children\":[[\"$\",\"$L3\",null,{}],[\"$\",\"$L4\",null,{}],[\"$\",\"div\",null,{\"className\":\"flex flex-1 min-h-0\",\"children\":[[\"$\",\"$L5\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":404}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}]]}]]}]}]}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L6\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L7\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L8\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0uz5svjlo9dwl.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L9\",null,{\"children\":[\"$\",\"$a\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@b\"}]}]]}],{},null,false,null]},null,false,\"$@c\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$Ld\",null,{\"children\":\"$Le\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lf\",null,{\"children\":[\"$\",\"$a\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L10\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$11\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/0tv48-.848bk7.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"WDRw2uNz8RE4CDa5ntj1v\"}\n"])</script><script>self.__next_f.push([1,"12:[]\nc:\"$W12\"\n"])</script><script>self.__next_f.push([1,"e:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"13:I[27201,[\"/_next/static/chunks/13fv-yi7.v52g.js\",\"/_next/static/chunks/0d3shmwh5_nmn.js\"],\"IconMark\"]\nb:null\n10:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.05o2q2p4kvnq_.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L13\",\"3\",{}]]\n"])</script></body></html>
|
package/out/setup.txt
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
1:"$Sreact.fragment"
|
|
2
|
-
2:I[52368,["/_next/static/chunks/
|
|
3
|
-
3:I[43688,["/_next/static/chunks/
|
|
4
|
-
4:I[26704,["/_next/static/chunks/
|
|
5
|
-
5:I[22140,["/_next/static/chunks/
|
|
6
|
-
6:I[39756,["/_next/static/chunks/
|
|
7
|
-
7:I[37457,["/_next/static/chunks/
|
|
8
|
-
8:I[94810,["/_next/static/chunks/
|
|
9
|
-
9:I[97367,["/_next/static/chunks/
|
|
2
|
+
2:I[52368,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"LocaleProvider"]
|
|
3
|
+
3:I[43688,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
4
|
+
4:I[26704,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
5
|
+
5:I[22140,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
6
|
+
6:I[39756,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
7
|
+
7:I[37457,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default"]
|
|
8
|
+
8:I[94810,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js","/_next/static/chunks/0uz5svjlo9dwl.js"],"default"]
|
|
9
|
+
9:I[97367,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"OutletBoundary"]
|
|
10
10
|
a:"$Sreact.suspense"
|
|
11
|
-
d:I[97367,["/_next/static/chunks/
|
|
12
|
-
f:I[97367,["/_next/static/chunks/
|
|
13
|
-
11:I[68027,["/_next/static/chunks/
|
|
14
|
-
:HL["/_next/static/chunks/
|
|
11
|
+
d:I[97367,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"ViewportBoundary"]
|
|
12
|
+
f:I[97367,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"MetadataBoundary"]
|
|
13
|
+
11:I[68027,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"default",1]
|
|
14
|
+
:HL["/_next/static/chunks/0tv48-.848bk7.css","style"]
|
|
15
15
|
:HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
16
|
-
0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/
|
|
16
|
+
0:{"P":null,"c":["","setup"],"q":"","i":false,"f":[[["",{"children":["setup",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",16],[["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0tv48-.848bk7.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/13fv-yi7.v52g.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0d3shmwh5_nmn.js","async":true,"nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex flex-col","children":["$","$L2",null,{"children":[["$","$L3",null,{}],["$","$L4",null,{}],["$","div",null,{"className":"flex flex-1 min-h-0","children":[["$","$L5",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L6",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]]}]]}]}]}]]}],{"children":[["$","$1","c",{"children":[null,["$","$L6",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L7",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L8",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0uz5svjlo9dwl.js","async":true,"nonce":"$undefined"}]],["$","$L9",null,{"children":["$","$a",null,{"name":"Next.MetadataOutlet","children":"$@b"}]}]]}],{},null,false,null]},null,false,"$@c"]},null,false,null],["$","$1","h",{"children":[null,["$","$Ld",null,{"children":"$Le"}],["$","div",null,{"hidden":true,"children":["$","$Lf",null,{"children":["$","$a",null,{"name":"Next.Metadata","children":"$L10"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$11",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0tv48-.848bk7.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"WDRw2uNz8RE4CDa5ntj1v"}
|
|
17
17
|
12:[]
|
|
18
18
|
c:"$W12"
|
|
19
19
|
e:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
|
20
|
-
13:I[27201,["/_next/static/chunks/
|
|
20
|
+
13:I[27201,["/_next/static/chunks/13fv-yi7.v52g.js","/_next/static/chunks/0d3shmwh5_nmn.js"],"IconMark"]
|
|
21
21
|
b:null
|
|
22
22
|
10:[["$","title","0",{"children":"QuadWork"}],["$","meta","1",{"name":"description","content":"Unified dashboard for multi-agent coding teams"}],["$","link","2",{"rel":"icon","href":"/favicon.ico?favicon.05o2q2p4kvnq_.ico","sizes":"256x256","type":"image/x-icon"}],["$","$L13","3",{}]]
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -41,6 +41,17 @@ app.get("/api/health", (_req, res) => {
|
|
|
41
41
|
res.json({ status: "ok" });
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
// --- Safe PTY write helper (#670) ---
|
|
45
|
+
|
|
46
|
+
function safeWrite(term, data) {
|
|
47
|
+
if (!term) return false;
|
|
48
|
+
try { term.write(data); return true; }
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err.code === "EIO") return false;
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
44
55
|
// --- CLI status detection ---
|
|
45
56
|
|
|
46
57
|
const { execFileSync } = require("child_process");
|
|
@@ -1305,6 +1316,88 @@ app.post("/api/agents/:project/reset", async (req, res) => {
|
|
|
1305
1316
|
}
|
|
1306
1317
|
});
|
|
1307
1318
|
|
|
1319
|
+
// --- Full Reset: restart all AC + agents across all projects (#657) ---
|
|
1320
|
+
|
|
1321
|
+
app.post("/api/full-reset", async (_req, res) => {
|
|
1322
|
+
const start = Date.now();
|
|
1323
|
+
console.log("[full-reset] starting...");
|
|
1324
|
+
try {
|
|
1325
|
+
const cfg = readConfig();
|
|
1326
|
+
const projects = (cfg.projects || []).filter((p) => !p.archived);
|
|
1327
|
+
|
|
1328
|
+
// 1. Stop all agent sessions
|
|
1329
|
+
console.log("[full-reset] stopping all agent sessions...");
|
|
1330
|
+
const sessionKeys = [...agentSessions.keys()];
|
|
1331
|
+
for (const key of sessionKeys) {
|
|
1332
|
+
await stopAgentSession(key);
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// 2. Stop Butler if running
|
|
1336
|
+
console.log("[full-reset] stopping Butler...");
|
|
1337
|
+
stopButlerPty();
|
|
1338
|
+
|
|
1339
|
+
// 3. Re-run startup migrations
|
|
1340
|
+
console.log("[full-reset] running startup migrations...");
|
|
1341
|
+
runStartupMigrations(cfg);
|
|
1342
|
+
|
|
1343
|
+
// 4. Restart each project's AC + agents
|
|
1344
|
+
let totalAgents = 0;
|
|
1345
|
+
const errors = [];
|
|
1346
|
+
for (const project of projects) {
|
|
1347
|
+
console.log(`[full-reset] restarting AC for ${project.id}...`);
|
|
1348
|
+
// Pre-mark reset as scheduled so AC restart's auto-reset timer is suppressed
|
|
1349
|
+
_acHealth.resetState.set(project.id, { status: "scheduled", timestamp: Date.now() });
|
|
1350
|
+
try {
|
|
1351
|
+
const acResp = await fetch(`http://127.0.0.1:${PORT}/api/agentchattr/${encodeURIComponent(project.id)}/restart`, {
|
|
1352
|
+
method: "POST",
|
|
1353
|
+
});
|
|
1354
|
+
if (!acResp.ok) {
|
|
1355
|
+
const errData = await acResp.json().catch(() => ({}));
|
|
1356
|
+
errors.push(`${project.id}: AC restart failed — ${errData.error || acResp.status}`);
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
} catch (err) {
|
|
1360
|
+
errors.push(`${project.id}: AC — ${err.message}`);
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
// Explicitly reset agents and await result
|
|
1364
|
+
try {
|
|
1365
|
+
const resetResp = await fetch(`http://127.0.0.1:${PORT}/api/agents/${encodeURIComponent(project.id)}/reset`, {
|
|
1366
|
+
method: "POST",
|
|
1367
|
+
});
|
|
1368
|
+
const resetData = await resetResp.json();
|
|
1369
|
+
if (resetData.ok) {
|
|
1370
|
+
totalAgents += resetData.restarted;
|
|
1371
|
+
} else {
|
|
1372
|
+
errors.push(`${project.id}: agent reset failed`);
|
|
1373
|
+
}
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
errors.push(`${project.id}: agent reset — ${err.message}`);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// 5. Restart Butler if enabled
|
|
1380
|
+
if (cfg.butler?.enabled) {
|
|
1381
|
+
console.log("[full-reset] restarting Butler...");
|
|
1382
|
+
const result = spawnButlerPty();
|
|
1383
|
+
if (!result.ok) errors.push(`butler: ${result.error}`);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
const duration = Date.now() - start;
|
|
1387
|
+
console.log(`[full-reset] complete in ${duration}ms — ${projects.length} projects, ${totalAgents} agents`);
|
|
1388
|
+
res.json({
|
|
1389
|
+
ok: errors.length === 0,
|
|
1390
|
+
projects: projects.length,
|
|
1391
|
+
agents: totalAgents,
|
|
1392
|
+
duration_ms: duration,
|
|
1393
|
+
...(errors.length > 0 ? { errors } : {}),
|
|
1394
|
+
});
|
|
1395
|
+
} catch (err) {
|
|
1396
|
+
console.error(`[full-reset] failed: ${err.message}`);
|
|
1397
|
+
res.status(500).json({ ok: false, error: err.message });
|
|
1398
|
+
}
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1308
1401
|
// --- Lifecycle: start spawns PTY (visible in terminal panel) ---
|
|
1309
1402
|
|
|
1310
1403
|
app.post("/api/agents/:project/:agent/start", async (req, res) => {
|
|
@@ -1473,6 +1566,23 @@ function spawnButlerPty() {
|
|
|
1473
1566
|
}
|
|
1474
1567
|
});
|
|
1475
1568
|
|
|
1569
|
+
// Auto-answer Claude's trust prompt if it appears within the first 10s
|
|
1570
|
+
if (command === "claude") {
|
|
1571
|
+
let trustHandled = false;
|
|
1572
|
+
const trustListener = term.onData((data) => {
|
|
1573
|
+
if (trustHandled) return;
|
|
1574
|
+
if (data.includes("trust") || data.includes("Yes,") || data.includes("1.")) {
|
|
1575
|
+
setTimeout(() => {
|
|
1576
|
+
if (!trustHandled && butlerSession.term === term) {
|
|
1577
|
+
safeWrite(term, "1\r");
|
|
1578
|
+
trustHandled = true;
|
|
1579
|
+
}
|
|
1580
|
+
}, 500);
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
setTimeout(() => { trustListener.dispose(); trustHandled = true; }, 10000);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1476
1586
|
term.onExit(({ exitCode }) => {
|
|
1477
1587
|
if (butlerSession.term === term) {
|
|
1478
1588
|
butlerSession.state = "stopped";
|
|
@@ -2069,7 +2179,7 @@ wss.on("connection:terminal", async (ws, req) => {
|
|
|
2069
2179
|
return;
|
|
2070
2180
|
}
|
|
2071
2181
|
} catch {}
|
|
2072
|
-
session.term
|
|
2182
|
+
safeWrite(session.term, str);
|
|
2073
2183
|
});
|
|
2074
2184
|
|
|
2075
2185
|
ws.on("close", () => {
|
|
@@ -2127,7 +2237,7 @@ wss.on("connection:butler", async (ws) => {
|
|
|
2127
2237
|
return;
|
|
2128
2238
|
}
|
|
2129
2239
|
} catch {}
|
|
2130
|
-
butlerSession.term
|
|
2240
|
+
safeWrite(butlerSession.term, str);
|
|
2131
2241
|
});
|
|
2132
2242
|
|
|
2133
2243
|
ws.on("close", () => {
|
|
@@ -2516,52 +2626,17 @@ function startAcHealthMonitor() {
|
|
|
2516
2626
|
console.log("[health] AC health monitor started (30s interval, per-project 60s grace)");
|
|
2517
2627
|
}
|
|
2518
2628
|
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
//
|
|
2525
|
-
|
|
2526
|
-
const startupCfg = readConfig();
|
|
2527
|
-
for (const p of (startupCfg.projects || [])) {
|
|
2528
|
-
const { url: acUrl } = resolveProjectChattr(p.id);
|
|
2529
|
-
const acPortMatch = acUrl.match(/:(\d+)/);
|
|
2530
|
-
const acPort = acPortMatch ? parseInt(acPortMatch[1], 10) : 8300;
|
|
2531
|
-
const alive = await isPortAlive(acPort);
|
|
2532
|
-
if (alive && !chattrProcesses.has(p.id)) {
|
|
2533
|
-
// AC is already running (e.g. spawned by cmdStart). Record it so
|
|
2534
|
-
// the health monitor can track it and the dashboard shows the
|
|
2535
|
-
// correct state. process is null because we don't own the child.
|
|
2536
|
-
chattrProcesses.set(p.id, { process: null, state: "running", error: null, runningSince: Date.now() });
|
|
2537
|
-
console.log(`[startup] ${p.id}: AC already alive on port ${acPort} — tracking`);
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
|
-
// Sync AgentChattr tokens for all projects on startup and backfill
|
|
2541
|
-
// the sender-overflow CSS/JS patch (#402) so already-running AC
|
|
2542
|
-
// instances receive the fix without requiring a restart.
|
|
2543
|
-
// #448: retry after 5s for projects where AC isn't up yet at boot.
|
|
2544
|
-
for (const p of (startupCfg.projects || [])) {
|
|
2545
|
-
syncChattrToken(p.id).catch(() => {
|
|
2546
|
-
setTimeout(() => syncChattrToken(p.id).catch(() => {}), 5000);
|
|
2547
|
-
});
|
|
2548
|
-
const { dir: acDir } = resolveProjectChattr(p.id);
|
|
2549
|
-
if (acDir) patchAgentchattrCss(acDir);
|
|
2550
|
-
}
|
|
2551
|
-
// #457: migrate bridge slugs in AC configs on startup.
|
|
2552
|
-
// Renames [agents.discord-bridge] → [agents.dc] and
|
|
2553
|
-
// [agents.telegram-bridge] → [agents.tg] so bridges register
|
|
2554
|
-
// under the short slug. Restarts AC ONLY for slug renames (not
|
|
2555
|
-
// fresh block appends) — #616: script-only patches should not
|
|
2556
|
-
// trigger AC restarts which kill bridge registration.
|
|
2557
|
-
for (const p of (startupCfg.projects || [])) {
|
|
2629
|
+
// #657: extracted startup migrations so full-reset can re-run them
|
|
2630
|
+
function runStartupMigrations(cfg) {
|
|
2631
|
+
const projects = (cfg.projects || []).filter((p) => !p.archived);
|
|
2632
|
+
const acRestartNeeded = [];
|
|
2633
|
+
|
|
2634
|
+
// bridge-migrate
|
|
2635
|
+
for (const p of projects) {
|
|
2558
2636
|
const acPath = projectAgentchattrConfigPath(p.id);
|
|
2559
2637
|
if (!fs.existsSync(acPath)) continue;
|
|
2560
2638
|
try {
|
|
2561
2639
|
const before = fs.readFileSync(acPath, "utf-8");
|
|
2562
|
-
// Track whether an actual slug RENAME happened (old → new).
|
|
2563
|
-
// Fresh block appends don't need an AC restart — AC picks them
|
|
2564
|
-
// up on its next natural start.
|
|
2565
2640
|
const hadOldDc = /^\[agents\.discord-bridge\]\s*$/m.test(before);
|
|
2566
2641
|
const hadOldTg = /^\[agents\.telegram-bridge\]\s*$/m.test(before);
|
|
2567
2642
|
const dc = patchAgentchattrConfigForDiscordBridge(before);
|
|
@@ -2569,28 +2644,14 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2569
2644
|
if (dc.changed || tg.changed) {
|
|
2570
2645
|
fs.writeFileSync(acPath, tg.text);
|
|
2571
2646
|
console.log(`[bridge-migrate] ${p.id}: migrated AC config slugs`);
|
|
2572
|
-
// Only restart AC when a slug was actually RENAMED — not when
|
|
2573
|
-
// a fresh block was appended (#616).
|
|
2574
2647
|
if (hadOldDc || hadOldTg) {
|
|
2575
|
-
|
|
2576
|
-
try {
|
|
2577
|
-
const r = await fetch(`http://127.0.0.1:${PORT}/api/agentchattr/${encodeURIComponent(p.id)}/restart`, {
|
|
2578
|
-
method: "POST",
|
|
2579
|
-
});
|
|
2580
|
-
if (r.ok) console.log(`[bridge-migrate] ${p.id}: restarted AC`);
|
|
2581
|
-
else console.warn(`[bridge-migrate] ${p.id}: AC restart returned ${r.status}`);
|
|
2582
|
-
} catch (err) {
|
|
2583
|
-
console.warn(`[bridge-migrate] ${p.id}: AC restart failed: ${err.message || err}`);
|
|
2584
|
-
}
|
|
2585
|
-
}, 3000);
|
|
2648
|
+
if (!acRestartNeeded.includes(p.id)) acRestartNeeded.push(p.id);
|
|
2586
2649
|
}
|
|
2587
2650
|
}
|
|
2588
2651
|
} catch {}
|
|
2589
2652
|
}
|
|
2590
|
-
|
|
2591
|
-
//
|
|
2592
|
-
// pattern. Without this, upgrading QuadWork leaves a stale on-disk script
|
|
2593
|
-
// missing fixes shipped in newer versions.
|
|
2653
|
+
|
|
2654
|
+
// bridge-refresh
|
|
2594
2655
|
const DISCORD_BRIDGE_SRC = path.join(__dirname, "..", "bridges", "discord", "discord_bridge.py");
|
|
2595
2656
|
const DISCORD_BRIDGE_DEST = path.join(os.homedir(), ".quadwork", "agentchattr-discord", "discord_bridge.py");
|
|
2596
2657
|
if (fs.existsSync(DISCORD_BRIDGE_SRC) && fs.existsSync(path.dirname(DISCORD_BRIDGE_DEST))) {
|
|
@@ -2601,10 +2662,8 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2601
2662
|
console.warn(`[bridge-refresh] failed to refresh Discord bridge script: ${err.message || err}`);
|
|
2602
2663
|
}
|
|
2603
2664
|
}
|
|
2604
|
-
|
|
2605
|
-
//
|
|
2606
|
-
// bridge scripts themselves may still have old defaults if the operator
|
|
2607
|
-
// upgraded QuadWork without re-installing the bridges.
|
|
2665
|
+
|
|
2666
|
+
// bridge slug patches
|
|
2608
2667
|
const BRIDGE_SLUG_PATCHES = [
|
|
2609
2668
|
{ file: path.join(os.homedir(), ".quadwork", "agentchattr-telegram", "telegram_bridge.py"), old: '"telegram-bridge"', replacement: '"tg"' },
|
|
2610
2669
|
{ file: path.join(os.homedir(), ".quadwork", "agentchattr-discord", "discord_bridge.py"), old: '"discord-bridge"', replacement: '"dc"' },
|
|
@@ -2618,20 +2677,15 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2618
2677
|
console.log(`[bridge-migrate] patched stale bridge_sender in ${path.basename(file)}`);
|
|
2619
2678
|
} catch {}
|
|
2620
2679
|
}
|
|
2621
|
-
|
|
2622
|
-
//
|
|
2623
|
-
// reviewer auth credentials and other site-specific customisations.
|
|
2680
|
+
|
|
2681
|
+
// reseed stale slugs
|
|
2624
2682
|
const SLUG_FIXES = [
|
|
2625
|
-
[/@reviewer1/g, "@re1"],
|
|
2626
|
-
[/@
|
|
2627
|
-
[/@
|
|
2628
|
-
[
|
|
2629
|
-
[/@t1\b/g, "@head"],
|
|
2630
|
-
[/@t3\b/g, "@dev"],
|
|
2631
|
-
[/\breviewer1\b/g, "re1"],
|
|
2632
|
-
[/\breviewer2\b/g, "re2"],
|
|
2683
|
+
[/@reviewer1/g, "@re1"], [/@reviewer2/g, "@re2"],
|
|
2684
|
+
[/@t2a/g, "@re1"], [/@t2b/g, "@re2"],
|
|
2685
|
+
[/@t1\b/g, "@head"], [/@t3\b/g, "@dev"],
|
|
2686
|
+
[/\breviewer1\b/g, "re1"], [/\breviewer2\b/g, "re2"],
|
|
2633
2687
|
];
|
|
2634
|
-
for (const p of
|
|
2688
|
+
for (const p of projects) {
|
|
2635
2689
|
if (!p.agents) continue;
|
|
2636
2690
|
for (const [agentId, agentCfg] of Object.entries(p.agents)) {
|
|
2637
2691
|
const wtDir = agentCfg.cwd;
|
|
@@ -2642,9 +2696,9 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2642
2696
|
try {
|
|
2643
2697
|
let content = fs.readFileSync(filePath, "utf-8");
|
|
2644
2698
|
let changed = false;
|
|
2645
|
-
for (const [pattern,
|
|
2699
|
+
for (const [pattern, repl] of SLUG_FIXES) {
|
|
2646
2700
|
const before = content;
|
|
2647
|
-
content = content.replace(pattern,
|
|
2701
|
+
content = content.replace(pattern, repl);
|
|
2648
2702
|
if (content !== before) changed = true;
|
|
2649
2703
|
}
|
|
2650
2704
|
if (changed) {
|
|
@@ -2657,30 +2711,23 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2657
2711
|
}
|
|
2658
2712
|
}
|
|
2659
2713
|
}
|
|
2660
|
-
|
|
2661
|
-
//
|
|
2662
|
-
for (const p of
|
|
2714
|
+
|
|
2715
|
+
// ghost-fix + idle-fix
|
|
2716
|
+
for (const p of projects) {
|
|
2663
2717
|
const acDir = resolveProjectChattr(p.id).dir;
|
|
2664
|
-
// Patch registry.py: add force parameter to register()
|
|
2665
2718
|
const regPath = path.join(acDir, "registry.py");
|
|
2666
2719
|
if (fs.existsSync(regPath)) {
|
|
2667
2720
|
try {
|
|
2668
2721
|
let reg = fs.readFileSync(regPath, "utf-8");
|
|
2669
2722
|
if (!reg.includes("force: bool")) {
|
|
2670
|
-
// Add force parameter to register() signature
|
|
2671
2723
|
reg = reg.replace(
|
|
2672
2724
|
/def register\(self, base: str, label: str \| None = None\) -> dict \| None:/,
|
|
2673
2725
|
"def register(self, base: str, label: str | None = None, force: bool = False) -> dict | None:",
|
|
2674
2726
|
);
|
|
2675
|
-
// Add force-replace logic after _expire_reserved()
|
|
2676
2727
|
reg = reg.replace(
|
|
2677
2728
|
" self._expire_reserved()\n\n # Find next free slot",
|
|
2678
2729
|
" self._expire_reserved()\n\n" +
|
|
2679
|
-
" # quadwork#478 + #502: force-replace
|
|
2680
|
-
" # for this base so the new registration always lands at slot 1.\n" +
|
|
2681
|
-
" # Also clear _reserved entries: after a crash-timeout the old name\n" +
|
|
2682
|
-
" # lives only in _reserved, so without this the grace period still\n" +
|
|
2683
|
-
" # blocks slot 1 and the agent gets a -2 suffix.\n" +
|
|
2730
|
+
" # quadwork#478 + #502: force-replace\n" +
|
|
2684
2731
|
" if force:\n" +
|
|
2685
2732
|
" ghosts = [n for n, i in self._instances.items() if i.base == base]\n" +
|
|
2686
2733
|
" for name in ghosts:\n" +
|
|
@@ -2694,7 +2741,6 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2694
2741
|
fs.writeFileSync(regPath, reg);
|
|
2695
2742
|
console.log(`[ghost-fix] ${p.id}: patched registry.py with force-replace support`);
|
|
2696
2743
|
} else if (!reg.includes("stale_reserved")) {
|
|
2697
|
-
// #502: upgrade existing force-replace patch to also clear _reserved
|
|
2698
2744
|
reg = reg.replace(
|
|
2699
2745
|
/( +)for name in ghosts:\n\1 del self\._instances\[name\]\n\1 self\._reserved\[name\] = time\.time\(\)/,
|
|
2700
2746
|
"$1for name in ghosts:\n$1 del self._instances[name]\n" +
|
|
@@ -2710,7 +2756,6 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2710
2756
|
console.warn(`[ghost-fix] ${p.id}: failed to patch registry.py: ${err.message}`);
|
|
2711
2757
|
}
|
|
2712
2758
|
}
|
|
2713
|
-
// Patch app.py: pass force from request body to registry.register()
|
|
2714
2759
|
const appPath = path.join(acDir, "app.py");
|
|
2715
2760
|
if (fs.existsSync(appPath)) {
|
|
2716
2761
|
try {
|
|
@@ -2727,33 +2772,22 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2727
2772
|
console.warn(`[ghost-fix] ${p.id}: failed to patch app.py: ${err.message}`);
|
|
2728
2773
|
}
|
|
2729
2774
|
}
|
|
2730
|
-
// #502 + #629: increase crash timeout from 15s to 120s.
|
|
2731
|
-
// Uses the shared patchCrashTimeout() from install-agentchattr.js.
|
|
2732
|
-
// For existing installs where AC is already running, the on-disk
|
|
2733
|
-
// patch alone is useless (Python caches module-level values at import).
|
|
2734
|
-
// Flag the project for AC restart so the running process picks it up.
|
|
2735
2775
|
if (fs.existsSync(appPath)) {
|
|
2736
2776
|
try {
|
|
2737
2777
|
const app = fs.readFileSync(appPath, "utf-8");
|
|
2738
2778
|
if (app.includes("_CRASH_TIMEOUT = 15")) {
|
|
2739
2779
|
patchCrashTimeout(acDir);
|
|
2740
|
-
console.log(`[idle-fix] ${p.id}: crash timeout patched on disk
|
|
2741
|
-
|
|
2742
|
-
startupCfg._acRestartNeeded.push(p.id);
|
|
2780
|
+
console.log(`[idle-fix] ${p.id}: crash timeout patched on disk`);
|
|
2781
|
+
acRestartNeeded.push(p.id);
|
|
2743
2782
|
}
|
|
2744
2783
|
} catch (err) {
|
|
2745
2784
|
console.warn(`[idle-fix] ${p.id}: failed to patch app.py crash timeout: ${err.message}`);
|
|
2746
2785
|
}
|
|
2747
2786
|
}
|
|
2748
2787
|
}
|
|
2749
|
-
|
|
2750
|
-
//
|
|
2751
|
-
|
|
2752
|
-
// drifts to HEAD, registration fails with "unknown base". This
|
|
2753
|
-
// migration appends [agents.claude]/[agents.codex] etc. sections so
|
|
2754
|
-
// HEAD AC accepts CLI-named bases. No AC restart needed — AC reads
|
|
2755
|
-
// config.toml on its own startup.
|
|
2756
|
-
for (const p of (startupCfg.projects || [])) {
|
|
2788
|
+
|
|
2789
|
+
// CLI-based agent sections
|
|
2790
|
+
for (const p of projects) {
|
|
2757
2791
|
const acPath = projectAgentchattrConfigPath(p.id);
|
|
2758
2792
|
if (!fs.existsSync(acPath)) continue;
|
|
2759
2793
|
try {
|
|
@@ -2780,6 +2814,44 @@ server.listen(PORT, "127.0.0.1", async () => {
|
|
|
2780
2814
|
console.warn(`[#596] ${p.id}: config.toml migration failed: ${err.message}`);
|
|
2781
2815
|
}
|
|
2782
2816
|
}
|
|
2817
|
+
|
|
2818
|
+
return acRestartNeeded;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
server.listen(PORT, "127.0.0.1", async () => {
|
|
2822
|
+
console.log(`QuadWork server listening on http://127.0.0.1:${PORT}`);
|
|
2823
|
+
syncTriggersFromConfig();
|
|
2824
|
+
// #579: detect AC processes already running (spawned by cmdStart before
|
|
2825
|
+
// the server module loaded). Without this, chattrProcesses is empty on
|
|
2826
|
+
// boot and the health monitor can't track cmdStart-spawned ACs, while
|
|
2827
|
+
// the dashboard's Start button would redundantly kill+respawn them.
|
|
2828
|
+
const startupCfg = readConfig();
|
|
2829
|
+
for (const p of (startupCfg.projects || [])) {
|
|
2830
|
+
const { url: acUrl } = resolveProjectChattr(p.id);
|
|
2831
|
+
const acPortMatch = acUrl.match(/:(\d+)/);
|
|
2832
|
+
const acPort = acPortMatch ? parseInt(acPortMatch[1], 10) : 8300;
|
|
2833
|
+
const alive = await isPortAlive(acPort);
|
|
2834
|
+
if (alive && !chattrProcesses.has(p.id)) {
|
|
2835
|
+
// AC is already running (e.g. spawned by cmdStart). Record it so
|
|
2836
|
+
// the health monitor can track it and the dashboard shows the
|
|
2837
|
+
// correct state. process is null because we don't own the child.
|
|
2838
|
+
chattrProcesses.set(p.id, { process: null, state: "running", error: null, runningSince: Date.now() });
|
|
2839
|
+
console.log(`[startup] ${p.id}: AC already alive on port ${acPort} — tracking`);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
// Sync AgentChattr tokens for all projects on startup and backfill
|
|
2843
|
+
// the sender-overflow CSS/JS patch (#402) so already-running AC
|
|
2844
|
+
// instances receive the fix without requiring a restart.
|
|
2845
|
+
// #448: retry after 5s for projects where AC isn't up yet at boot.
|
|
2846
|
+
for (const p of (startupCfg.projects || [])) {
|
|
2847
|
+
syncChattrToken(p.id).catch(() => {
|
|
2848
|
+
setTimeout(() => syncChattrToken(p.id).catch(() => {}), 5000);
|
|
2849
|
+
});
|
|
2850
|
+
const { dir: acDir } = resolveProjectChattr(p.id);
|
|
2851
|
+
if (acDir) patchAgentchattrCss(acDir);
|
|
2852
|
+
}
|
|
2853
|
+
const acRestartNeeded = runStartupMigrations(startupCfg);
|
|
2854
|
+
startupCfg._acRestartNeeded = acRestartNeeded.length > 0 ? acRestartNeeded : undefined;
|
|
2783
2855
|
// #629: restart AC for projects where idle-fix patched the on-disk file
|
|
2784
2856
|
// so the running Python process picks up _CRASH_TIMEOUT = 120.
|
|
2785
2857
|
// Use port-alive check instead of chattrProcesses — AC may be running
|
package/server/routes.js
CHANGED
|
@@ -1798,6 +1798,20 @@ function summarizeItems(items) {
|
|
|
1798
1798
|
return parts.join(" · ");
|
|
1799
1799
|
}
|
|
1800
1800
|
|
|
1801
|
+
router.get("/api/batch-active", (req, res) => {
|
|
1802
|
+
const projectId = req.query.project;
|
|
1803
|
+
if (!projectId) return res.status(400).json({ error: "Missing project" });
|
|
1804
|
+
if (!getRepo(projectId)) return res.status(400).json({ error: "No repo configured for project" });
|
|
1805
|
+
const queuePath = path.join(CONFIG_DIR, projectId, "OVERNIGHT-QUEUE.md");
|
|
1806
|
+
let active = false;
|
|
1807
|
+
try {
|
|
1808
|
+
const text = fs.readFileSync(queuePath, "utf-8");
|
|
1809
|
+
const { issueNumbers } = parseActiveBatch(text);
|
|
1810
|
+
active = issueNumbers.length > 0;
|
|
1811
|
+
} catch {}
|
|
1812
|
+
return res.json({ active });
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1801
1815
|
router.get("/api/batch-progress", async (req, res) => {
|
|
1802
1816
|
const projectId = req.query.project;
|
|
1803
1817
|
if (!projectId) return res.status(400).json({ error: "Missing project" });
|