quadwork 1.0.17 → 1.1.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/README.md +28 -0
- package/bin/quadwork.js +436 -52
- package/out/404.html +1 -1
- package/out/__next.__PAGE__.txt +1 -1
- package/out/__next._full.txt +1 -1
- package/out/__next._head.txt +1 -1
- package/out/__next._index.txt +1 -1
- package/out/__next._tree.txt +1 -1
- package/out/_not-found/__next._full.txt +1 -1
- package/out/_not-found/__next._head.txt +1 -1
- package/out/_not-found/__next._index.txt +1 -1
- package/out/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/out/_not-found/__next._not-found.txt +1 -1
- package/out/_not-found/__next._tree.txt +1 -1
- package/out/_not-found.html +1 -1
- package/out/_not-found.txt +1 -1
- package/out/app-shell/__next._full.txt +1 -1
- package/out/app-shell/__next._head.txt +1 -1
- package/out/app-shell/__next._index.txt +1 -1
- package/out/app-shell/__next._tree.txt +1 -1
- package/out/app-shell/__next.app-shell.__PAGE__.txt +1 -1
- package/out/app-shell/__next.app-shell.txt +1 -1
- package/out/app-shell.html +1 -1
- package/out/app-shell.txt +1 -1
- package/out/index.html +1 -1
- package/out/index.txt +1 -1
- package/out/project/_/__next._full.txt +1 -1
- package/out/project/_/__next._head.txt +1 -1
- package/out/project/_/__next._index.txt +1 -1
- package/out/project/_/__next._tree.txt +1 -1
- package/out/project/_/__next.project.$d$id.__PAGE__.txt +1 -1
- package/out/project/_/__next.project.$d$id.txt +1 -1
- package/out/project/_/__next.project.txt +1 -1
- package/out/project/_/memory/__next._full.txt +1 -1
- package/out/project/_/memory/__next._head.txt +1 -1
- package/out/project/_/memory/__next._index.txt +1 -1
- package/out/project/_/memory/__next._tree.txt +1 -1
- package/out/project/_/memory/__next.project.$d$id.memory.__PAGE__.txt +1 -1
- package/out/project/_/memory/__next.project.$d$id.memory.txt +1 -1
- package/out/project/_/memory/__next.project.$d$id.txt +1 -1
- package/out/project/_/memory/__next.project.txt +1 -1
- package/out/project/_/memory.html +1 -1
- package/out/project/_/memory.txt +1 -1
- package/out/project/_/queue/__next._full.txt +1 -1
- package/out/project/_/queue/__next._head.txt +1 -1
- package/out/project/_/queue/__next._index.txt +1 -1
- package/out/project/_/queue/__next._tree.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.queue.__PAGE__.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.queue.txt +1 -1
- package/out/project/_/queue/__next.project.$d$id.txt +1 -1
- package/out/project/_/queue/__next.project.txt +1 -1
- package/out/project/_/queue.html +1 -1
- package/out/project/_/queue.txt +1 -1
- package/out/project/_.html +1 -1
- package/out/project/_.txt +1 -1
- package/out/settings/__next._full.txt +1 -1
- package/out/settings/__next._head.txt +1 -1
- package/out/settings/__next._index.txt +1 -1
- package/out/settings/__next._tree.txt +1 -1
- package/out/settings/__next.settings.__PAGE__.txt +1 -1
- package/out/settings/__next.settings.txt +1 -1
- package/out/settings.html +1 -1
- package/out/settings.txt +1 -1
- package/out/setup/__next._full.txt +1 -1
- package/out/setup/__next._head.txt +1 -1
- package/out/setup/__next._index.txt +1 -1
- package/out/setup/__next._tree.txt +1 -1
- package/out/setup/__next.setup.__PAGE__.txt +1 -1
- package/out/setup/__next.setup.txt +1 -1
- package/out/setup.html +1 -1
- package/out/setup.txt +1 -1
- package/package.json +1 -1
- package/server/config.js +22 -1
- package/server/index.js +13 -4
- package/server/install-agentchattr.js +215 -0
- package/server/routes.js +38 -3
- /package/out/_next/static/{TKQFu1hHpaRuo62RWWrUJ → zx5_zAjM3qhPvkFrygZp8}/_buildManifest.js +0 -0
- /package/out/_next/static/{TKQFu1hHpaRuo62RWWrUJ → zx5_zAjM3qhPvkFrygZp8}/_clientMiddlewareManifest.js +0 -0
- /package/out/_next/static/{TKQFu1hHpaRuo62RWWrUJ → zx5_zAjM3qhPvkFrygZp8}/_ssgManifest.js +0 -0
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
3:I[12527,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
|
|
4
4
|
4:I[59763,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
|
|
5
5
|
:HL["/_next/static/chunks/10b3c4k.q.yw..css","style"]
|
|
6
|
-
0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/10b3c4k.q.yw..css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"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."}]}]]}]}]],[]]}]}]]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"
|
|
6
|
+
0:{"rsc":["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/10b3c4k.q.yw..css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.js","async":true}]],["$","html",null,{"lang":"en","className":"geist_mono_8d43a2aa-module__8Li5zG__variable h-full","children":["$","body",null,{"className":"h-full flex","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","template":["$","$L4",null,{}],"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."}]}]]}]}]],[]]}]}]]}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"zx5_zAjM3qhPvkFrygZp8"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
:HL["/_next/static/chunks/10b3c4k.q.yw..css","style"]
|
|
2
2
|
:HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
3
|
-
0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"setup","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"
|
|
3
|
+
0:{"tree":{"name":"","param":null,"prefetchHints":16,"slots":{"children":{"name":"setup","param":null,"prefetchHints":0,"slots":{"children":{"name":"__PAGE__","param":null,"prefetchHints":0,"slots":null}}}}},"staleTime":300,"buildId":"zx5_zAjM3qhPvkFrygZp8"}
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
2:I[64618,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js","/_next/static/chunks/0caq73v0knw_w.js"],"default"]
|
|
3
3
|
3:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"OutletBoundary"]
|
|
4
4
|
4:"$Sreact.suspense"
|
|
5
|
-
0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0caq73v0knw_w.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"
|
|
5
|
+
0:{"rsc":["$","$1","c",{"children":[["$","$L2",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0caq73v0knw_w.js","async":true}]],["$","$L3",null,{"children":["$","$4",null,{"name":"Next.MetadataOutlet","children":"$@5"}]}]]}],"isPartial":false,"staleTime":300,"varyParams":null,"buildId":"zx5_zAjM3qhPvkFrygZp8"}
|
|
6
6
|
5:null
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
2:I[12527,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
|
|
3
3
|
3:I[59763,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default"]
|
|
4
4
|
4:[]
|
|
5
|
-
0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"
|
|
5
|
+
0:{"rsc":["$","$1","c",{"children":[null,["$","$L2",null,{"parallelRouterKey":"children","template":["$","$L3",null,{}]}]]}],"isPartial":false,"staleTime":300,"varyParams":"$W4","buildId":"zx5_zAjM3qhPvkFrygZp8"}
|
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/10b3c4k.q.yw..css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0z~0.4hivi.f2.js"/><script src="/_next/static/chunks/0ezniz80psxr6.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/0io_y3d0p5v~b.js" async=""></script><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/turbopack-0sammtvunroor.js" async=""></script><script src="/_next/static/chunks/08fgie1bcjynm.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><script src="/_next/static/chunks/0caq73v0knw_w.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.0x3dzn~oxb6tn.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex"><div hidden=""><!--$--><!--/$--></div><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors 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="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" 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="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors 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></aside><main class="flex-1 min-w-0 overflow-auto"><div class="h-full overflow-y-auto"><div class="px-6 pt-6 pb-4 border-b border-border"><h1 class="text-lg font-semibold text-text tracking-tight">Set Up Your AI Dev Team</h1><p class="text-[11px] text-text-muted mt-1">Configure agents, connect your repo, and launch a multi-agent development workflow in minutes.</p></div><div class="flex h-[calc(100%-80px)]"><div class="flex-1 flex gap-6 p-6 overflow-y-auto"><div class="w-44 shrink-0"><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-accent text-accent bg-accent/10">1</span><div><span class="text-[11px] block leading-tight text-text font-semibold">Project Name</span><span class="text-[10px] text-text-muted block">Name your project</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">2</span><div><span class="text-[11px] block leading-tight text-text-muted">GitHub Repo</span><span class="text-[10px] text-text-muted block">Connect a repository</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">3</span><div><span class="text-[11px] block leading-tight text-text-muted">Agent Models</span><span class="text-[10px] text-text-muted block">Configure CLI backends</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">4</span><div><span class="text-[11px] block leading-tight text-text-muted">Working Directory</span><span class="text-[10px] text-text-muted block">Set the local path</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">5</span><div><span class="text-[11px] block leading-tight text-text-muted">Create Workspaces</span><span class="text-[10px] text-text-muted block">Worktrees + seed files</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">6</span><div><span class="text-[11px] block leading-tight text-text-muted">Ready to Launch</span><span class="text-[10px] text-text-muted block">Review & start</span></div></div></div><div class="flex-1 border border-border p-5 min-h-0"><div><h2 class="text-sm font-semibold text-text mb-1">Name your project</h2><p class="text-[11px] text-text-muted mb-4">This name identifies your project in the dashboard and agent configs.</p><input placeholder="e.g. My DeFi App" class="w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-4" autofocus="" value=""/><button disabled="" class="px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50">Next</button></div></div></div><div class="w-64 shrink-0 border-l border-border p-4 overflow-y-auto bg-bg-surface/50"><h3 class="text-[11px] font-semibold text-text-muted uppercase tracking-wider mb-3">Configuration Preview</h3><div class="space-y-3 text-[11px]"><div><span class="text-text-muted block mb-0.5">Project</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Repository</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Backends</span><div class="flex justify-between"><span class="text-text capitalize">head</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer1</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer2</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">dev</span><span class="text-accent">claude</span></div></div><div><span class="text-text-muted block mb-0.5">Directory</span><span class="text-text font-mono text-[10px]">—</span></div><div><span class="text-text-muted block mb-0.5">Status</span><div class="space-y-0.5"><div class="flex items-center gap-1.5"><span class="text-[10px] text-text">●</span><span class="text-[10px] text-text">Project Name</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">GitHub Repo</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Agent Models</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Working Directory</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Create Workspaces</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Ready to Launch</span></div></div></div></div></div></div></div><!--$--><!--/$--></main><script src="/_next/static/chunks/0z~0.4hivi.f2.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[86081,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[12527,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[59763,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[64618,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\",\"/_next/static/chunks/0caq73v0knw_w.js\"],\"default\"]\n6:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\na:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nc:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\ne:I[92243,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/10b3c4k.q.yw..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/10b3c4k.q.yw..css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/08fgie1bcjynm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0ox7p_szjhn69.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\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",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,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L5\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0caq73v0knw_w.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,null]},null,false,\"$@9\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Ld\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/10b3c4k.q.yw..css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"TKQFu1hHpaRuo62RWWrUJ\"}\n"])</script><script>self.__next_f.push([1,"f:[]\n9:\"$Wf\"\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"10:I[80070,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n8:null\nd:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L10\",\"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/10b3c4k.q.yw..css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/0z~0.4hivi.f2.js"/><script src="/_next/static/chunks/0ezniz80psxr6.js" async=""></script><script src="/_next/static/chunks/0r7t_sj_sejq9.js" async=""></script><script src="/_next/static/chunks/0io_y3d0p5v~b.js" async=""></script><script src="/_next/static/chunks/0excsn2a_5qsb.js" async=""></script><script src="/_next/static/chunks/turbopack-0sammtvunroor.js" async=""></script><script src="/_next/static/chunks/08fgie1bcjynm.js" async=""></script><script src="/_next/static/chunks/0ox7p_szjhn69.js" async=""></script><script src="/_next/static/chunks/0caq73v0knw_w.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.0x3dzn~oxb6tn.ico" sizes="256x256" type="image/x-icon"/><script src="/_next/static/chunks/03~yq9q893hmn.js" noModule=""></script></head><body class="h-full flex"><div hidden=""><!--$--><!--/$--></div><aside class="w-16 shrink-0 h-full border-r border-border bg-bg-surface flex flex-col items-center py-3"><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors 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="w-6 h-px bg-border my-2"></div><div class="flex-1 flex flex-col items-center gap-2 overflow-y-auto min-h-0"><a class="w-10 h-10 flex items-center justify-center rounded-full border border-dashed border-border text-text-muted hover:text-text hover:bg-[#1a1a1a] transition-colors" 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="w-6 h-px bg-border my-2"></div><a class="w-10 h-10 flex items-center justify-center rounded-sm transition-colors 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></aside><main class="flex-1 min-w-0 overflow-auto"><div class="h-full overflow-y-auto"><div class="px-6 pt-6 pb-4 border-b border-border"><h1 class="text-lg font-semibold text-text tracking-tight">Set Up Your AI Dev Team</h1><p class="text-[11px] text-text-muted mt-1">Configure agents, connect your repo, and launch a multi-agent development workflow in minutes.</p></div><div class="flex h-[calc(100%-80px)]"><div class="flex-1 flex gap-6 p-6 overflow-y-auto"><div class="w-44 shrink-0"><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-accent text-accent bg-accent/10">1</span><div><span class="text-[11px] block leading-tight text-text font-semibold">Project Name</span><span class="text-[10px] text-text-muted block">Name your project</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">2</span><div><span class="text-[11px] block leading-tight text-text-muted">GitHub Repo</span><span class="text-[10px] text-text-muted block">Connect a repository</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">3</span><div><span class="text-[11px] block leading-tight text-text-muted">Agent Models</span><span class="text-[10px] text-text-muted block">Configure CLI backends</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">4</span><div><span class="text-[11px] block leading-tight text-text-muted">Working Directory</span><span class="text-[10px] text-text-muted block">Set the local path</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">5</span><div><span class="text-[11px] block leading-tight text-text-muted">Create Workspaces</span><span class="text-[10px] text-text-muted block">Worktrees + seed files</span></div></div><div class="flex items-start gap-2 py-2"><span class="w-5 h-5 flex items-center justify-center text-[10px] border shrink-0 mt-0.5 border-border text-text-muted">6</span><div><span class="text-[11px] block leading-tight text-text-muted">Ready to Launch</span><span class="text-[10px] text-text-muted block">Review & start</span></div></div></div><div class="flex-1 border border-border p-5 min-h-0"><div><h2 class="text-sm font-semibold text-text mb-1">Name your project</h2><p class="text-[11px] text-text-muted mb-4">This name identifies your project in the dashboard and agent configs.</p><input placeholder="e.g. My DeFi App" class="w-full bg-transparent border border-border px-2 py-1.5 text-[12px] text-text outline-none focus:border-accent mb-4" autofocus="" value=""/><button disabled="" class="px-4 py-1.5 bg-accent text-bg text-[12px] font-semibold hover:bg-accent-dim transition-colors disabled:opacity-50">Next</button></div></div></div><div class="w-64 shrink-0 border-l border-border p-4 overflow-y-auto bg-bg-surface/50"><h3 class="text-[11px] font-semibold text-text-muted uppercase tracking-wider mb-3">Configuration Preview</h3><div class="space-y-3 text-[11px]"><div><span class="text-text-muted block mb-0.5">Project</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Repository</span><span class="text-text">—</span></div><div><span class="text-text-muted block mb-0.5">Backends</span><div class="flex justify-between"><span class="text-text capitalize">head</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer1</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">reviewer2</span><span class="text-accent">claude</span></div><div class="flex justify-between"><span class="text-text capitalize">dev</span><span class="text-accent">claude</span></div></div><div><span class="text-text-muted block mb-0.5">Directory</span><span class="text-text font-mono text-[10px]">—</span></div><div><span class="text-text-muted block mb-0.5">Status</span><div class="space-y-0.5"><div class="flex items-center gap-1.5"><span class="text-[10px] text-text">●</span><span class="text-[10px] text-text">Project Name</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">GitHub Repo</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Agent Models</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Working Directory</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Create Workspaces</span></div><div class="flex items-center gap-1.5"><span class="text-[10px] text-text-muted">○</span><span class="text-[10px] text-text-muted">Ready to Launch</span></div></div></div></div></div></div></div><!--$--><!--/$--></main><script src="/_next/static/chunks/0z~0.4hivi.f2.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[86081,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n3:I[12527,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n4:I[59763,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\"]\n5:I[64618,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\",\"/_next/static/chunks/0caq73v0knw_w.js\"],\"default\"]\n6:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"OutletBoundary\"]\n7:\"$Sreact.suspense\"\na:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"ViewportBoundary\"]\nc:I[11717,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"MetadataBoundary\"]\ne:I[92243,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"default\",1]\n:HL[\"/_next/static/chunks/10b3c4k.q.yw..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/10b3c4k.q.yw..css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/08fgie1bcjynm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/chunks/0ox7p_szjhn69.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\",\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"main\",null,{\"className\":\"flex-1 min-w-0 overflow-auto\",\"children\":[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",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,[\"$\",\"$L3\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L4\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"$L5\",null,{}],[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/chunks/0caq73v0knw_w.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L6\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@8\"}]}]]}],{},null,false,null]},null,false,\"$@9\"]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$La\",null,{\"children\":\"$Lb\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Lc\",null,{\"children\":[\"$\",\"$7\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Ld\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$e\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/chunks/10b3c4k.q.yw..css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"zx5_zAjM3qhPvkFrygZp8\"}\n"])</script><script>self.__next_f.push([1,"f:[]\n9:\"$Wf\"\n"])</script><script>self.__next_f.push([1,"b:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"10:I[80070,[\"/_next/static/chunks/08fgie1bcjynm.js\",\"/_next/static/chunks/0ox7p_szjhn69.js\"],\"IconMark\"]\n8:null\nd:[[\"$\",\"title\",\"0\",{\"children\":\"QuadWork\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Unified dashboard for multi-agent coding teams\"}],[\"$\",\"link\",\"2\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.0x3dzn~oxb6tn.ico\",\"sizes\":\"256x256\",\"type\":\"image/x-icon\"}],[\"$\",\"$L10\",\"3\",{}]]\n"])</script></body></html>
|
package/out/setup.txt
CHANGED
|
@@ -10,7 +10,7 @@ c:I[11717,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_s
|
|
|
10
10
|
e:I[92243,["/_next/static/chunks/08fgie1bcjynm.js","/_next/static/chunks/0ox7p_szjhn69.js"],"default",1]
|
|
11
11
|
:HL["/_next/static/chunks/10b3c4k.q.yw..css","style"]
|
|
12
12
|
:HL["/_next/static/media/797e433ab948586e-s.p.0.q-h669a_dqa.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
|
|
13
|
-
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/10b3c4k.q.yw..css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.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","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",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,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0caq73v0knw_w.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,"$@9"]},null,false,null],["$","$1","h",{"children":[null,["$","$La",null,{"children":"$Lb"}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Ld"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$e",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/10b3c4k.q.yw..css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"
|
|
13
|
+
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/10b3c4k.q.yw..css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","script","script-0",{"src":"/_next/static/chunks/08fgie1bcjynm.js","async":true,"nonce":"$undefined"}],["$","script","script-1",{"src":"/_next/static/chunks/0ox7p_szjhn69.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","children":[["$","$L2",null,{}],["$","main",null,{"className":"flex-1 min-w-0 overflow-auto","children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",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,["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":[["$","$1","c",{"children":[["$","$L5",null,{}],[["$","script","script-0",{"src":"/_next/static/chunks/0caq73v0knw_w.js","async":true,"nonce":"$undefined"}]],["$","$L6",null,{"children":["$","$7",null,{"name":"Next.MetadataOutlet","children":"$@8"}]}]]}],{},null,false,null]},null,false,"$@9"]},null,false,null],["$","$1","h",{"children":[null,["$","$La",null,{"children":"$Lb"}],["$","div",null,{"hidden":true,"children":["$","$Lc",null,{"children":["$","$7",null,{"name":"Next.Metadata","children":"$Ld"}]}]}],["$","meta",null,{"name":"next-size-adjust","content":""}]]}],false]],"m":"$undefined","G":["$e",[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/10b3c4k.q.yw..css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]]],"S":true,"h":null,"s":"$undefined","l":"$undefined","p":"$undefined","d":"$undefined","b":"zx5_zAjM3qhPvkFrygZp8"}
|
|
14
14
|
f:[]
|
|
15
15
|
9:"$Wf"
|
|
16
16
|
b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
|
package/package.json
CHANGED
package/server/config.js
CHANGED
|
@@ -93,12 +93,33 @@ function resolveAgentCommand(projectId, agentId) {
|
|
|
93
93
|
function resolveProjectChattr(projectId) {
|
|
94
94
|
const config = readConfig();
|
|
95
95
|
const project = projectId ? config.projects?.find((p) => p.id === projectId) : null;
|
|
96
|
+
|
|
97
|
+
// Resolution order for AgentChattr install dir:
|
|
98
|
+
// 1. project.agentchattr_dir — per-project clone (Option B, #181)
|
|
99
|
+
// 2. config.agentchattr_dir — legacy global clone (v1 backward compat)
|
|
100
|
+
// 3. ~/.quadwork/{projectId}/agentchattr — per-project default
|
|
101
|
+
//
|
|
102
|
+
// Phase 1A (#182) is schema-only: project.agentchattr_dir is now written
|
|
103
|
+
// on every new project, but the actual clone-on-create logic does not
|
|
104
|
+
// land until #183/#184/#185. Until then, if the project field points at
|
|
105
|
+
// a directory that does not yet contain a working install, fall back to
|
|
106
|
+
// the legacy global so existing setups (and brand-new projects on a v1
|
|
107
|
+
// host) keep starting AgentChattr from the working clone.
|
|
108
|
+
const perProjectDefault = projectId
|
|
109
|
+
? path.join(os.homedir(), ".quadwork", projectId, "agentchattr")
|
|
110
|
+
: path.join(os.homedir(), ".quadwork", "agentchattr");
|
|
111
|
+
const legacyGlobal = config.agentchattr_dir || path.join(os.homedir(), ".quadwork", "agentchattr");
|
|
112
|
+
let dir = project?.agentchattr_dir || legacyGlobal || perProjectDefault;
|
|
113
|
+
if (!fs.existsSync(path.join(dir, "run.py")) && fs.existsSync(path.join(legacyGlobal, "run.py"))) {
|
|
114
|
+
dir = legacyGlobal;
|
|
115
|
+
}
|
|
116
|
+
|
|
96
117
|
return {
|
|
97
118
|
url: project?.agentchattr_url || config.agentchattr_url || "http://127.0.0.1:8300",
|
|
98
119
|
token: project?.agentchattr_token || config.agentchattr_token || null,
|
|
99
120
|
mcp_http_port: project?.mcp_http_port || null,
|
|
100
121
|
mcp_sse_port: project?.mcp_sse_port || null,
|
|
101
|
-
dir
|
|
122
|
+
dir,
|
|
102
123
|
};
|
|
103
124
|
}
|
|
104
125
|
|
package/server/index.js
CHANGED
|
@@ -398,12 +398,21 @@ async function handleAgentChattr(req, res) {
|
|
|
398
398
|
const { url: chattrUrl } = resolveProjectChattr(projectId);
|
|
399
399
|
const chattrPort = new URL(chattrUrl).port || "8300";
|
|
400
400
|
|
|
401
|
-
// Find per-project config.toml
|
|
401
|
+
// Find per-project config.toml. Phase 2E / #181: prefer the
|
|
402
|
+
// per-project AgentChattr clone ROOT (where the web/CLI wizards now
|
|
403
|
+
// write it as of #184/#185 — and where run.py actually reads it from).
|
|
404
|
+
// Fall back to the legacy <working_dir>/agentchattr/config.toml for
|
|
405
|
+
// v1 setups that haven't been migrated yet (#188).
|
|
402
406
|
const cfg = readConfig();
|
|
403
407
|
const project = cfg.projects?.find((p) => p.id === projectId);
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
408
|
+
const { dir: resolvedAcDir } = resolveProjectChattr(projectId);
|
|
409
|
+
let projectConfigToml = null;
|
|
410
|
+
if (resolvedAcDir && fs.existsSync(path.join(resolvedAcDir, "config.toml"))) {
|
|
411
|
+
projectConfigToml = path.join(resolvedAcDir, "config.toml");
|
|
412
|
+
} else if (project?.working_dir) {
|
|
413
|
+
const legacyToml = path.join(project.working_dir, "agentchattr", "config.toml");
|
|
414
|
+
if (fs.existsSync(legacyToml)) projectConfigToml = legacyToml;
|
|
415
|
+
}
|
|
407
416
|
|
|
408
417
|
function getProc() {
|
|
409
418
|
return chattrProcesses.get(projectId) || { process: null, state: "stopped", error: null };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// Shared AgentChattr install helper used by both the CLI wizard
|
|
2
|
+
// (bin/quadwork.js) and the web setup route (server/routes.js).
|
|
3
|
+
//
|
|
4
|
+
// Extracted as part of #185 (Phase 2D of master #181) so the web UI
|
|
5
|
+
// can clone AgentChattr per-project without duplicating the locking,
|
|
6
|
+
// idempotency, and cleanup-safety logic that #183 + #187 added.
|
|
7
|
+
//
|
|
8
|
+
// Public API:
|
|
9
|
+
// findAgentChattr(dir) → string|null
|
|
10
|
+
// installAgentChattr(dir) → string|null (.lastError on failure)
|
|
11
|
+
// chattrSpawnArgs(dir, extraArgs) → { command, spawnArgs, cwd } | null
|
|
12
|
+
// AGENTCHATTR_REPO → upstream URL constant
|
|
13
|
+
//
|
|
14
|
+
// Self-contained — depends only on Node built-ins so it's safe to require
|
|
15
|
+
// from anywhere in the project (CLI bin, server routes, future tests).
|
|
16
|
+
|
|
17
|
+
const { execSync } = require("child_process");
|
|
18
|
+
const fs = require("fs");
|
|
19
|
+
const path = require("path");
|
|
20
|
+
|
|
21
|
+
const AGENTCHATTR_REPO = "https://github.com/bcurts/agentchattr.git";
|
|
22
|
+
|
|
23
|
+
// Stale-lock thresholds for installAgentChattr().
|
|
24
|
+
// Lock files older than this OR whose owning pid is no longer alive are
|
|
25
|
+
// treated as crashed and reclaimed. Tuned to comfortably exceed the longest
|
|
26
|
+
// step (pip install of agentchattr requirements, ~120s timeout).
|
|
27
|
+
const INSTALL_LOCK_STALE_MS = 10 * 60 * 1000; // 10 min
|
|
28
|
+
const INSTALL_LOCK_WAIT_TOTAL_MS = 30 * 1000; // wait up to 30s for a peer
|
|
29
|
+
const INSTALL_LOCK_POLL_MS = 500;
|
|
30
|
+
|
|
31
|
+
function _run(cmd, opts = {}) {
|
|
32
|
+
try { return execSync(cmd, { encoding: "utf-8", stdio: "pipe", ...opts }).trim(); }
|
|
33
|
+
catch { return null; }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function _isPidAlive(pid) {
|
|
37
|
+
if (!pid || !Number.isFinite(pid)) return false;
|
|
38
|
+
try { process.kill(pid, 0); return true; }
|
|
39
|
+
catch (e) { return e.code === "EPERM"; }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function _readLock(lockFile) {
|
|
43
|
+
try {
|
|
44
|
+
const raw = fs.readFileSync(lockFile, "utf-8").trim();
|
|
45
|
+
const [pidStr, tsStr] = raw.split(":");
|
|
46
|
+
return { pid: parseInt(pidStr, 10), ts: parseInt(tsStr, 10) || 0 };
|
|
47
|
+
} catch { return null; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _isLockStale(lockFile) {
|
|
51
|
+
const info = _readLock(lockFile);
|
|
52
|
+
if (!info) return true;
|
|
53
|
+
if (Date.now() - info.ts > INSTALL_LOCK_STALE_MS) return true;
|
|
54
|
+
if (!_isPidAlive(info.pid)) return true;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if AgentChattr is fully installed (cloned + venv ready) at `dir`.
|
|
60
|
+
* Returns the directory path if both run.py and .venv/bin/python exist, or null.
|
|
61
|
+
* Caller must pass an explicit `dir` — there is no default.
|
|
62
|
+
*/
|
|
63
|
+
function findAgentChattr(dir) {
|
|
64
|
+
if (!dir) return null;
|
|
65
|
+
if (fs.existsSync(path.join(dir, "run.py")) && fs.existsSync(path.join(dir, ".venv", "bin", "python"))) return dir;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Clone AgentChattr and set up its venv at `dir`. Idempotent — safe to
|
|
71
|
+
* re-run on the same path, and safe to call repeatedly with different
|
|
72
|
+
* paths in the same process. Designed to support per-project clones (#181).
|
|
73
|
+
*
|
|
74
|
+
* Behavior on re-run:
|
|
75
|
+
* - Fully-installed path → no-op (skips clone, skips venv create, skips pip)
|
|
76
|
+
* - Missing run.py → clones (only after refusing to overwrite
|
|
77
|
+
* unrelated content; see safety rules below)
|
|
78
|
+
* - Missing venv → creates venv and reinstalls requirements
|
|
79
|
+
*
|
|
80
|
+
* Safety rules — never accidentally clean up unrelated directories:
|
|
81
|
+
* - Empty dir → safe to remove
|
|
82
|
+
* - Git repo whose origin contains "agentchattr" → safe to remove
|
|
83
|
+
* - Anything else → refuse, return null
|
|
84
|
+
*
|
|
85
|
+
* Concurrency: a per-target lock at `${dir}.install.lock` serializes
|
|
86
|
+
* concurrent installs to the same path. Stale locks (dead pid OR older
|
|
87
|
+
* than 10 min) are reclaimed atomically via rename → unlink. Live
|
|
88
|
+
* peers are polled for up to 30s; after that, returns null with a
|
|
89
|
+
* clear lastError.
|
|
90
|
+
*
|
|
91
|
+
* On failure, returns null and stores a human-readable reason on
|
|
92
|
+
* `installAgentChattr.lastError` so callers can surface it without
|
|
93
|
+
* changing the return shape.
|
|
94
|
+
*/
|
|
95
|
+
function installAgentChattr(dir) {
|
|
96
|
+
if (!dir) {
|
|
97
|
+
installAgentChattr.lastError = "installAgentChattr: dir is required";
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
installAgentChattr.lastError = null;
|
|
101
|
+
const setError = (msg) => { installAgentChattr.lastError = msg; return null; };
|
|
102
|
+
|
|
103
|
+
// --- Per-target lock ---
|
|
104
|
+
const lockFile = `${dir}.install.lock`;
|
|
105
|
+
try { fs.mkdirSync(path.dirname(lockFile), { recursive: true }); }
|
|
106
|
+
catch (e) { return setError(`Cannot create parent of ${dir}: ${e.message}`); }
|
|
107
|
+
|
|
108
|
+
let acquired = false;
|
|
109
|
+
const deadline = Date.now() + INSTALL_LOCK_WAIT_TOTAL_MS;
|
|
110
|
+
while (!acquired) {
|
|
111
|
+
try {
|
|
112
|
+
fs.writeFileSync(lockFile, `${process.pid}:${Date.now()}`, { flag: "wx" });
|
|
113
|
+
acquired = true;
|
|
114
|
+
} catch (e) {
|
|
115
|
+
if (e.code !== "EEXIST") return setError(`Cannot create install lock ${lockFile}: ${e.message}`);
|
|
116
|
+
if (_isLockStale(lockFile)) {
|
|
117
|
+
const sideline = `${lockFile}.stale.${process.pid}.${Date.now()}`;
|
|
118
|
+
try {
|
|
119
|
+
fs.renameSync(lockFile, sideline);
|
|
120
|
+
try { fs.unlinkSync(sideline); } catch {}
|
|
121
|
+
} catch (renameErr) {
|
|
122
|
+
if (renameErr.code !== "ENOENT") {
|
|
123
|
+
return setError(`Cannot reclaim stale lock ${lockFile}: ${renameErr.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (Date.now() >= deadline) {
|
|
129
|
+
const info = _readLock(lockFile) || { pid: "?", ts: 0 };
|
|
130
|
+
return setError(`Another install is in progress at ${dir} (pid ${info.pid}); timed out after ${INSTALL_LOCK_WAIT_TOTAL_MS}ms. Re-run after it finishes, or remove ${lockFile} if stale.`);
|
|
131
|
+
}
|
|
132
|
+
try { execSync(`sleep ${INSTALL_LOCK_POLL_MS / 1000}`); }
|
|
133
|
+
catch { /* sleep interrupted; loop will recheck */ }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
return _installAgentChattrLocked(dir, setError);
|
|
139
|
+
} finally {
|
|
140
|
+
try { fs.unlinkSync(lockFile); } catch {}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
installAgentChattr.lastError = null;
|
|
144
|
+
|
|
145
|
+
function _installAgentChattrLocked(dir, setError) {
|
|
146
|
+
const runPy = path.join(dir, "run.py");
|
|
147
|
+
const venvPython = path.join(dir, ".venv", "bin", "python");
|
|
148
|
+
let venvJustCreated = false;
|
|
149
|
+
|
|
150
|
+
// 1. Clone if run.py is missing.
|
|
151
|
+
if (!fs.existsSync(runPy)) {
|
|
152
|
+
if (fs.existsSync(dir)) {
|
|
153
|
+
let entries;
|
|
154
|
+
try { entries = fs.readdirSync(dir); }
|
|
155
|
+
catch (e) { return setError(`Cannot read ${dir}: ${e.message}`); }
|
|
156
|
+
const isEmpty = entries.length === 0;
|
|
157
|
+
if (isEmpty) {
|
|
158
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); }
|
|
159
|
+
catch (e) { return setError(`Cannot remove empty dir ${dir}: ${e.message}`); }
|
|
160
|
+
} else if (fs.existsSync(path.join(dir, ".git"))) {
|
|
161
|
+
const remote = _run(`git -C "${dir}" remote get-url origin 2>/dev/null`);
|
|
162
|
+
if (remote && remote.includes("agentchattr")) {
|
|
163
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); }
|
|
164
|
+
catch (e) { return setError(`Cannot remove failed clone at ${dir}: ${e.message}`); }
|
|
165
|
+
} else {
|
|
166
|
+
return setError(`Refusing to overwrite ${dir}: contains a non-AgentChattr git repo`);
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
return setError(`Refusing to overwrite ${dir}: directory exists with unrelated content`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
try { fs.mkdirSync(path.dirname(dir), { recursive: true }); }
|
|
173
|
+
catch (e) { return setError(`Cannot create parent of ${dir}: ${e.message}`); }
|
|
174
|
+
const cloneResult = _run(`git clone "${AGENTCHATTR_REPO}" "${dir}" 2>&1`, { timeout: 60000 });
|
|
175
|
+
if (cloneResult === null) return setError(`git clone of ${AGENTCHATTR_REPO} into ${dir} failed`);
|
|
176
|
+
if (!fs.existsSync(runPy)) return setError(`Clone completed but run.py missing at ${dir}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 2. Create venv if missing.
|
|
180
|
+
if (!fs.existsSync(venvPython)) {
|
|
181
|
+
const venvResult = _run(`python3 -m venv "${path.join(dir, ".venv")}" 2>&1`, { timeout: 60000 });
|
|
182
|
+
if (venvResult === null) return setError(`python3 -m venv failed at ${dir}/.venv (is python3 installed?)`);
|
|
183
|
+
if (!fs.existsSync(venvPython)) return setError(`venv created but ${venvPython} missing`);
|
|
184
|
+
venvJustCreated = true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// 3. Install requirements only when the venv was just (re)created.
|
|
188
|
+
if (venvJustCreated) {
|
|
189
|
+
const reqFile = path.join(dir, "requirements.txt");
|
|
190
|
+
if (fs.existsSync(reqFile)) {
|
|
191
|
+
const pipResult = _run(`"${venvPython}" -m pip install -r "${reqFile}" 2>&1`, { timeout: 120000 });
|
|
192
|
+
if (pipResult === null) return setError(`pip install -r ${reqFile} failed`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return dir;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Get spawn args for launching AgentChattr from its cloned directory.
|
|
200
|
+
* Returns { command, spawnArgs, cwd } or null if not fully installed.
|
|
201
|
+
* Requires .venv/bin/python — never falls back to bare python3.
|
|
202
|
+
*/
|
|
203
|
+
function chattrSpawnArgs(dir, extraArgs) {
|
|
204
|
+
if (!dir) return null;
|
|
205
|
+
const venvPython = path.join(dir, ".venv", "bin", "python");
|
|
206
|
+
if (!fs.existsSync(path.join(dir, "run.py")) || !fs.existsSync(venvPython)) return null;
|
|
207
|
+
return { command: venvPython, spawnArgs: ["run.py", ...(extraArgs || [])], cwd: dir };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = {
|
|
211
|
+
AGENTCHATTR_REPO,
|
|
212
|
+
findAgentChattr,
|
|
213
|
+
installAgentChattr,
|
|
214
|
+
chattrSpawnArgs,
|
|
215
|
+
};
|
package/server/routes.js
CHANGED
|
@@ -69,6 +69,7 @@ router.put("/api/config", (req, res) => {
|
|
|
69
69
|
// ─── Chat (AgentChattr proxy) ──────────────────────────────────────────────
|
|
70
70
|
|
|
71
71
|
const { resolveProjectChattr } = require("./config");
|
|
72
|
+
const { installAgentChattr, findAgentChattr } = require("./install-agentchattr");
|
|
72
73
|
|
|
73
74
|
function getChattrConfig(projectId) {
|
|
74
75
|
const resolved = resolveProjectChattr(projectId);
|
|
@@ -650,9 +651,28 @@ router.post("/api/setup", (req, res) => {
|
|
|
650
651
|
const parentDir = path.dirname(workingDir);
|
|
651
652
|
const backends = body.backends;
|
|
652
653
|
|
|
653
|
-
//
|
|
654
|
-
|
|
655
|
-
|
|
654
|
+
// Phase 2D / #181: config.toml lives at the per-project AgentChattr
|
|
655
|
+
// clone ROOT (~/.quadwork/{id}/agentchattr/), not inside the user's
|
|
656
|
+
// project working_dir. AgentChattr's run.py loads ROOT/config.toml
|
|
657
|
+
// and ignores --config, so the toml has to be at the same path the
|
|
658
|
+
// clone lives at. Same path matches what writeQuadWorkConfig()
|
|
659
|
+
// persists in agentchattr_dir (#182) and what the CLI wizard
|
|
660
|
+
// writes (#184).
|
|
661
|
+
//
|
|
662
|
+
// We install the clone *here*, before writing config.toml. The
|
|
663
|
+
// install must run first because installAgentChattr() refuses to
|
|
664
|
+
// overwrite a non-empty directory it doesn't recognize — if we
|
|
665
|
+
// mkdir + write config.toml first, the subsequent install in
|
|
666
|
+
// add-config would see "unrelated content" and reject the dir,
|
|
667
|
+
// breaking first-run web project creation (t2a's review of #195).
|
|
668
|
+
const projectConfigDir = path.join(CONFIG_DIR, dirName, "agentchattr");
|
|
669
|
+
if (!findAgentChattr(projectConfigDir)) {
|
|
670
|
+
const installResult = installAgentChattr(projectConfigDir);
|
|
671
|
+
if (!installResult) {
|
|
672
|
+
const reason = installAgentChattr.lastError || "unknown error";
|
|
673
|
+
return res.json({ ok: false, error: `AgentChattr install failed at ${projectConfigDir}: ${reason}` });
|
|
674
|
+
}
|
|
675
|
+
}
|
|
656
676
|
const dataDir = path.join(projectConfigDir, "data");
|
|
657
677
|
fs.mkdirSync(dataDir, { recursive: true });
|
|
658
678
|
const tomlPath = path.join(projectConfigDir, "config.toml");
|
|
@@ -743,12 +763,27 @@ router.post("/api/setup", (req, res) => {
|
|
|
743
763
|
while (usedMcpPorts.has(mcp_sse_port)) mcp_sse_port++;
|
|
744
764
|
}
|
|
745
765
|
if (!agentchattr_token) agentchattr_token = crypto.randomBytes(16).toString("hex");
|
|
766
|
+
|
|
767
|
+
// Phase 2D / #181: clone AgentChattr per-project before saving config.
|
|
768
|
+
// The path here must match the one written into agentchattr_dir below
|
|
769
|
+
// and the one agentchattr-config writes config.toml into.
|
|
770
|
+
const perProjectDir = path.join(CONFIG_DIR, id, "agentchattr");
|
|
771
|
+
if (!findAgentChattr(perProjectDir)) {
|
|
772
|
+
const installResult = installAgentChattr(perProjectDir);
|
|
773
|
+
if (!installResult) {
|
|
774
|
+
const reason = installAgentChattr.lastError || "unknown error";
|
|
775
|
+
return res.json({ ok: false, error: `AgentChattr install failed at ${perProjectDir}: ${reason}` });
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
746
779
|
cfg.projects.push({
|
|
747
780
|
id, name, repo, working_dir: workingDir, agents,
|
|
748
781
|
agentchattr_url: `http://127.0.0.1:${chattrPort}`,
|
|
749
782
|
agentchattr_token,
|
|
750
783
|
mcp_http_port,
|
|
751
784
|
mcp_sse_port,
|
|
785
|
+
// Per-project AgentChattr clone path (Option B / #181).
|
|
786
|
+
agentchattr_dir: perProjectDir,
|
|
752
787
|
});
|
|
753
788
|
const dir = path.dirname(CONFIG_PATH);
|
|
754
789
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
File without changes
|
|
File without changes
|
|
File without changes
|