groove-dev 0.27.71 → 0.27.73
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/node_modules/@groove-dev/cli/package.json +1 -1
- package/node_modules/@groove-dev/daemon/package.json +1 -1
- package/node_modules/@groove-dev/daemon/src/api.js +38 -27
- package/node_modules/@groove-dev/daemon/src/providers/claude-code.js +2 -0
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +1 -0
- package/node_modules/@groove-dev/daemon/src/providers/index.js +3 -3
- package/node_modules/@groove-dev/gui/dist/assets/index-BFc7Ov6v.css +1 -0
- package/node_modules/@groove-dev/gui/dist/assets/{index-BK6tvmxx.js → index-Deza1S0i.js} +160 -160
- package/node_modules/@groove-dev/gui/dist/index.html +2 -2
- package/node_modules/@groove-dev/gui/package.json +1 -1
- package/node_modules/@groove-dev/gui/src/components/editor/editor-tabs.jsx +5 -5
- package/node_modules/@groove-dev/gui/src/components/editor/file-tree.jsx +11 -0
- package/node_modules/@groove-dev/gui/src/components/settings/ProviderSetupWizard.jsx +7 -2
- package/node_modules/@groove-dev/gui/src/stores/groove.js +46 -54
- package/node_modules/@groove-dev/gui/src/views/editor.jsx +4 -3
- package/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/daemon/package.json +1 -1
- package/packages/daemon/src/api.js +38 -27
- package/packages/daemon/src/providers/claude-code.js +2 -0
- package/packages/daemon/src/providers/codex.js +1 -0
- package/packages/daemon/src/providers/index.js +3 -3
- package/packages/gui/dist/assets/index-BFc7Ov6v.css +1 -0
- package/packages/gui/dist/assets/{index-BK6tvmxx.js → index-Deza1S0i.js} +160 -160
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/components/editor/editor-tabs.jsx +5 -5
- package/packages/gui/src/components/editor/file-tree.jsx +11 -0
- package/packages/gui/src/components/settings/ProviderSetupWizard.jsx +7 -2
- package/packages/gui/src/stores/groove.js +46 -54
- package/packages/gui/src/views/editor.jsx +4 -3
- package/node_modules/@groove-dev/gui/dist/assets/index-74E3YTkT.css +0 -1
- package/packages/gui/dist/assets/index-74E3YTkT.css +0 -1
- package/packages/gui/node_modules/.vite/deps/@codemirror_autocomplete.js +0 -68
- package/packages/gui/node_modules/.vite/deps/@codemirror_autocomplete.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_commands.js +0 -1420
- package/packages/gui/node_modules/.vite/deps/@codemirror_commands.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-css.js +0 -17
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-css.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js +0 -22
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-html.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js +0 -34
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-javascript.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-json.js +0 -101
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-json.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js +0 -2534
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-markdown.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js +0 -789
- package/packages/gui/node_modules/.vite/deps/@codemirror_lang-python.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_language.js +0 -115
- package/packages/gui/node_modules/.vite/deps/@codemirror_language.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_search.js +0 -1136
- package/packages/gui/node_modules/.vite/deps/@codemirror_search.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_state.js +0 -63
- package/packages/gui/node_modules/.vite/deps/@codemirror_state.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_theme-one-dark.js +0 -179
- package/packages/gui/node_modules/.vite/deps/@codemirror_theme-one-dark.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@codemirror_view.js +0 -104
- package/packages/gui/node_modules/.vite/deps/@codemirror_view.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-context-menu.js +0 -1202
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-context-menu.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-dialog.js +0 -365
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-dialog.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-scroll-area.js +0 -757
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-scroll-area.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tabs.js +0 -219
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tabs.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tooltip.js +0 -562
- package/packages/gui/node_modules/.vite/deps/@radix-ui_react-tooltip.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-fit.js +0 -46
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-fit.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-web-links.js +0 -121
- package/packages/gui/node_modules/.vite/deps/@xterm_addon-web-links.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@xterm_xterm.js +0 -9237
- package/packages/gui/node_modules/.vite/deps/@xterm_xterm.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/@xyflow_react.js +0 -9934
- package/packages/gui/node_modules/.vite/deps/@xyflow_react.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/_metadata.json +0 -259
- package/packages/gui/node_modules/.vite/deps/chunk-3EE34IFC.js +0 -5169
- package/packages/gui/node_modules/.vite/deps/chunk-3EE34IFC.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-3IB5EUP7.js +0 -2000
- package/packages/gui/node_modules/.vite/deps/chunk-3IB5EUP7.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-44CLUOQE.js +0 -1776
- package/packages/gui/node_modules/.vite/deps/chunk-44CLUOQE.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-4JHIKDUF.js +0 -435
- package/packages/gui/node_modules/.vite/deps/chunk-4JHIKDUF.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-ACEP5X2F.js +0 -292
- package/packages/gui/node_modules/.vite/deps/chunk-ACEP5X2F.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-CDLO3JFW.js +0 -1004
- package/packages/gui/node_modules/.vite/deps/chunk-CDLO3JFW.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-FOR2E6IR.js +0 -105
- package/packages/gui/node_modules/.vite/deps/chunk-FOR2E6IR.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-HPXOV7QR.js +0 -23
- package/packages/gui/node_modules/.vite/deps/chunk-HPXOV7QR.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-HVFOBSCQ.js +0 -1062
- package/packages/gui/node_modules/.vite/deps/chunk-HVFOBSCQ.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-PGL3TTJA.js +0 -1115
- package/packages/gui/node_modules/.vite/deps/chunk-PGL3TTJA.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-PGMATLPZ.js +0 -2294
- package/packages/gui/node_modules/.vite/deps/chunk-PGMATLPZ.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-PR4QN5HX.js +0 -42
- package/packages/gui/node_modules/.vite/deps/chunk-PR4QN5HX.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-QX3PPTYO.js +0 -315
- package/packages/gui/node_modules/.vite/deps/chunk-QX3PPTYO.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-RE2FU7ZU.js +0 -10985
- package/packages/gui/node_modules/.vite/deps/chunk-RE2FU7ZU.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-SPKVQGZX.js +0 -701
- package/packages/gui/node_modules/.vite/deps/chunk-SPKVQGZX.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-YAD7VOVN.js +0 -280
- package/packages/gui/node_modules/.vite/deps/chunk-YAD7VOVN.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-YEUURE4V.js +0 -264
- package/packages/gui/node_modules/.vite/deps/chunk-YEUURE4V.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-YYJMNVCJ.js +0 -3459
- package/packages/gui/node_modules/.vite/deps/chunk-YYJMNVCJ.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/chunk-Z5NIFSCT.js +0 -1106
- package/packages/gui/node_modules/.vite/deps/chunk-Z5NIFSCT.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/clsx.js +0 -22
- package/packages/gui/node_modules/.vite/deps/clsx.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/framer-motion.js +0 -12732
- package/packages/gui/node_modules/.vite/deps/framer-motion.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/lucide-react.js +0 -29653
- package/packages/gui/node_modules/.vite/deps/lucide-react.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/package.json +0 -3
- package/packages/gui/node_modules/.vite/deps/react-dom.js +0 -6
- package/packages/gui/node_modules/.vite/deps/react-dom.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/react-dom_client.js +0 -20217
- package/packages/gui/node_modules/.vite/deps/react-dom_client.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/react.js +0 -5
- package/packages/gui/node_modules/.vite/deps/react.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/react_jsx-dev-runtime.js +0 -278
- package/packages/gui/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/react_jsx-runtime.js +0 -6
- package/packages/gui/node_modules/.vite/deps/react_jsx-runtime.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/tailwind-merge.js +0 -3263
- package/packages/gui/node_modules/.vite/deps/tailwind-merge.js.map +0 -7
- package/packages/gui/node_modules/.vite/deps/zustand.js +0 -56
- package/packages/gui/node_modules/.vite/deps/zustand.js.map +0 -7
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
8
8
|
<title>Groove GUI</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-Deza1S0i.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-C0HXlhrU.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/reactflow-BQPfi37R.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/codemirror-BBL3i_JW.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/xterm--7_ns2zW.js">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BFc7Ov6v.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -55,14 +55,14 @@ export function EditorTabs() {
|
|
|
55
55
|
if (openTabs.length === 0) return null;
|
|
56
56
|
|
|
57
57
|
return (
|
|
58
|
-
<div className="flex items-
|
|
58
|
+
<div className="flex items-stretch h-9 bg-surface-3 border-b border-border-subtle flex-shrink-0">
|
|
59
59
|
{overflows && (
|
|
60
60
|
<button onClick={scrollLeft} className="flex-shrink-0 px-1 h-full text-text-4 hover:text-text-1 hover:bg-surface-4 transition-colors cursor-pointer">
|
|
61
61
|
<ChevronLeft size={14} />
|
|
62
62
|
</button>
|
|
63
63
|
)}
|
|
64
64
|
|
|
65
|
-
<div ref={scrollRef} className="flex items-
|
|
65
|
+
<div ref={scrollRef} className="flex items-stretch flex-1 min-w-0 overflow-x-auto scrollbar-none scroll-smooth" style={{ scrollSnapType: 'x mandatory' }}>
|
|
66
66
|
{openTabs.map((path) => {
|
|
67
67
|
const isActive = path === activeFile;
|
|
68
68
|
const file = files[path];
|
|
@@ -74,12 +74,12 @@ export function EditorTabs() {
|
|
|
74
74
|
<ContextMenuTrigger asChild>
|
|
75
75
|
<div
|
|
76
76
|
className={cn(
|
|
77
|
-
'flex items-center gap-1.5
|
|
77
|
+
'flex items-center gap-1.5 px-3 text-xs font-sans cursor-pointer select-none',
|
|
78
78
|
'border-r border-white/5',
|
|
79
79
|
'transition-colors duration-75 flex-shrink-0',
|
|
80
80
|
isActive
|
|
81
|
-
? 'bg-surface-
|
|
82
|
-
: 'bg-surface-3 text-text-4 hover:text-text-1 hover:bg-surface-4',
|
|
81
|
+
? 'bg-surface-0 text-text-0 border-b border-b-accent'
|
|
82
|
+
: 'bg-surface-3 text-text-4 hover:text-text-1 hover:bg-surface-4 border-b border-b-transparent',
|
|
83
83
|
)}
|
|
84
84
|
style={{ scrollSnapAlign: 'start' }}
|
|
85
85
|
onClick={() => setActiveFile(path)}
|
|
@@ -431,6 +431,17 @@ export function FileTree({ rootDir }) {
|
|
|
431
431
|
)}
|
|
432
432
|
</div>
|
|
433
433
|
))}
|
|
434
|
+
{rootEntries.length === 0 && !filter && (
|
|
435
|
+
<div className="px-3 py-6 text-center">
|
|
436
|
+
<p className="text-xs text-text-4">No files found</p>
|
|
437
|
+
<button
|
|
438
|
+
onClick={() => fetchTreeDir('')}
|
|
439
|
+
className="mt-2 text-xs text-accent hover:underline cursor-pointer"
|
|
440
|
+
>
|
|
441
|
+
Retry
|
|
442
|
+
</button>
|
|
443
|
+
</div>
|
|
444
|
+
)}
|
|
434
445
|
</div>
|
|
435
446
|
</ScrollArea>
|
|
436
447
|
|
|
@@ -123,7 +123,7 @@ function InstallStep({ providerId, meta }) {
|
|
|
123
123
|
'text-2xs font-sans',
|
|
124
124
|
hasError ? 'text-danger' : isDone ? 'text-success' : 'text-text-3',
|
|
125
125
|
)}>
|
|
126
|
-
{hasError ? 'Something went wrong' : isDone ? 'Installed successfully' : progress?.message || 'Preparing...'}
|
|
126
|
+
{hasError ? (typeof hasError === 'string' ? hasError : 'Something went wrong') : isDone ? 'Installed successfully' : progress?.message || 'Preparing...'}
|
|
127
127
|
</span>
|
|
128
128
|
{isInstalling && (
|
|
129
129
|
<button
|
|
@@ -145,7 +145,12 @@ function InstallStep({ providerId, meta }) {
|
|
|
145
145
|
|
|
146
146
|
{hasError && (
|
|
147
147
|
<div className="space-y-3">
|
|
148
|
-
|
|
148
|
+
{typeof hasError === 'string' && hasError.length > 40 && (
|
|
149
|
+
<div className="p-3 bg-surface-0 border border-border-subtle rounded-md max-h-24 overflow-y-auto">
|
|
150
|
+
<p className="text-2xs font-mono text-danger/80 whitespace-pre-wrap break-all">{hasError}</p>
|
|
151
|
+
</div>
|
|
152
|
+
)}
|
|
153
|
+
<p className="text-xs text-text-2 font-sans">Check that npm is in your PATH and try again.</p>
|
|
149
154
|
<Button
|
|
150
155
|
variant="primary"
|
|
151
156
|
size="sm"
|
|
@@ -1471,17 +1471,21 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1471
1471
|
projectDir: data.projectDir,
|
|
1472
1472
|
recentProjects: data.recentProjects || [],
|
|
1473
1473
|
showProjectPicker: isHome || (data.recentProjects || []).length === 0,
|
|
1474
|
+
editorTreeCache: {},
|
|
1474
1475
|
});
|
|
1475
1476
|
} catch {}
|
|
1476
1477
|
},
|
|
1477
1478
|
|
|
1478
1479
|
async setProjectDir(path) {
|
|
1479
1480
|
const data = await api.post('/project-dir', { path });
|
|
1481
|
+
try { await api.post('/files/root', { root: data.projectDir }); } catch {}
|
|
1480
1482
|
set({
|
|
1481
1483
|
projectDir: data.projectDir,
|
|
1482
1484
|
recentProjects: data.recentProjects || [],
|
|
1483
1485
|
showProjectPicker: false,
|
|
1486
|
+
editorTreeCache: {},
|
|
1484
1487
|
});
|
|
1488
|
+
get().fetchTreeDir('');
|
|
1485
1489
|
},
|
|
1486
1490
|
|
|
1487
1491
|
toggleProjectPicker() {
|
|
@@ -1652,12 +1656,15 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1652
1656
|
providerInstallProgress: {},
|
|
1653
1657
|
|
|
1654
1658
|
async installProvider(providerId) {
|
|
1655
|
-
set((s) => ({
|
|
1659
|
+
const update = (patch) => set((s) => ({
|
|
1656
1660
|
providerInstallProgress: {
|
|
1657
1661
|
...s.providerInstallProgress,
|
|
1658
|
-
[providerId]: {
|
|
1662
|
+
[providerId]: { ...s.providerInstallProgress[providerId], ...patch },
|
|
1659
1663
|
},
|
|
1660
1664
|
}));
|
|
1665
|
+
|
|
1666
|
+
update({ installing: true, percent: 0, message: 'Starting install...', error: null, done: false });
|
|
1667
|
+
|
|
1661
1668
|
try {
|
|
1662
1669
|
const res = await fetch(`/api/providers/${encodeURIComponent(providerId)}/install`, {
|
|
1663
1670
|
method: 'POST',
|
|
@@ -1667,59 +1674,41 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
1667
1674
|
const err = await res.text();
|
|
1668
1675
|
throw new Error(err || `Install failed (${res.status})`);
|
|
1669
1676
|
}
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
while (true) {
|
|
1677
|
-
const { done, value } = await reader.read();
|
|
1678
|
-
if (done) break;
|
|
1679
|
-
buf += decoder.decode(value, { stream: true });
|
|
1680
|
-
const lines = buf.split('\n');
|
|
1681
|
-
buf = lines.pop();
|
|
1682
|
-
for (const line of lines) {
|
|
1683
|
-
if (!line.trim()) continue;
|
|
1684
|
-
try {
|
|
1685
|
-
const ev = JSON.parse(line);
|
|
1686
|
-
const isError = ev.status === 'error';
|
|
1687
|
-
const isDone = ev.status === 'complete';
|
|
1688
|
-
if (isError) lastError = ev.output || 'Install failed';
|
|
1689
|
-
set((s) => ({
|
|
1690
|
-
providerInstallProgress: {
|
|
1691
|
-
...s.providerInstallProgress,
|
|
1692
|
-
[providerId]: {
|
|
1693
|
-
...s.providerInstallProgress[providerId],
|
|
1694
|
-
percent: ev.progress ?? s.providerInstallProgress[providerId]?.percent ?? 0,
|
|
1695
|
-
message: ev.output || s.providerInstallProgress[providerId]?.message,
|
|
1696
|
-
error: isError ? (ev.output || 'Install failed') : null,
|
|
1697
|
-
done: isDone,
|
|
1698
|
-
installing: !isDone && !isError,
|
|
1699
|
-
},
|
|
1700
|
-
},
|
|
1701
|
-
}));
|
|
1702
|
-
} catch { /* skip malformed NDJSON line */ }
|
|
1703
|
-
}
|
|
1704
|
-
}
|
|
1705
|
-
if (lastError) throw new Error(lastError);
|
|
1677
|
+
|
|
1678
|
+
let body;
|
|
1679
|
+
try {
|
|
1680
|
+
body = await res.text();
|
|
1681
|
+
} catch (e) {
|
|
1682
|
+
throw new Error(`Failed to read response: ${e.message}`);
|
|
1706
1683
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1684
|
+
|
|
1685
|
+
let lastError = null;
|
|
1686
|
+
let completed = false;
|
|
1687
|
+
for (const line of body.split('\n')) {
|
|
1688
|
+
if (!line.trim()) continue;
|
|
1689
|
+
try {
|
|
1690
|
+
const ev = JSON.parse(line);
|
|
1691
|
+
const isError = ev.status === 'error';
|
|
1692
|
+
const isDone = ev.status === 'complete';
|
|
1693
|
+
if (isError) lastError = ev.output || 'Install failed';
|
|
1694
|
+
if (isDone) completed = true;
|
|
1695
|
+
update({
|
|
1696
|
+
percent: ev.progress ?? get().providerInstallProgress[providerId]?.percent ?? 0,
|
|
1697
|
+
message: ev.output || get().providerInstallProgress[providerId]?.message,
|
|
1698
|
+
error: isError ? (ev.output || 'Install failed') : null,
|
|
1699
|
+
done: isDone,
|
|
1700
|
+
installing: !isDone && !isError,
|
|
1701
|
+
});
|
|
1702
|
+
} catch { /* skip malformed line */ }
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
if (lastError) throw new Error(lastError);
|
|
1706
|
+
if (!completed) throw new Error(body.slice(0, 500) || 'Install ended without confirmation');
|
|
1707
|
+
|
|
1708
|
+
update({ installing: false, percent: 100, message: 'Installed', error: null, done: true });
|
|
1715
1709
|
get().addToast('success', `${providerId} installed`);
|
|
1716
1710
|
} catch (err) {
|
|
1717
|
-
|
|
1718
|
-
providerInstallProgress: {
|
|
1719
|
-
...s.providerInstallProgress,
|
|
1720
|
-
[providerId]: { installing: false, percent: 0, message: null, error: err.message, done: false },
|
|
1721
|
-
},
|
|
1722
|
-
}));
|
|
1711
|
+
update({ installing: false, percent: 0, message: null, error: err.message, done: false });
|
|
1723
1712
|
get().addToast('error', `Install failed: ${providerId}`, err.message);
|
|
1724
1713
|
throw err;
|
|
1725
1714
|
}
|
|
@@ -2082,8 +2071,11 @@ export const useGrooveStore = create((set, get) => ({
|
|
|
2082
2071
|
async fetchTreeDir(dirPath) {
|
|
2083
2072
|
try {
|
|
2084
2073
|
const data = await api.get(`/files/tree?path=${encodeURIComponent(dirPath)}`);
|
|
2085
|
-
set((s) => ({ editorTreeCache: { ...s.editorTreeCache, [dirPath]: data.entries } }));
|
|
2086
|
-
} catch {
|
|
2074
|
+
set((s) => ({ editorTreeCache: { ...s.editorTreeCache, [dirPath]: data.entries || [] } }));
|
|
2075
|
+
} catch (err) {
|
|
2076
|
+
console.error('[file-tree] fetchTreeDir failed for', dirPath, err.message);
|
|
2077
|
+
set((s) => ({ editorTreeCache: { ...s.editorTreeCache, [dirPath]: [] } }));
|
|
2078
|
+
}
|
|
2087
2079
|
},
|
|
2088
2080
|
|
|
2089
2081
|
async createFile(relPath) {
|
|
@@ -33,6 +33,7 @@ export default function EditorView() {
|
|
|
33
33
|
const sidebarWidth = useGrooveStore((s) => s.editorSidebarWidth);
|
|
34
34
|
const setSidebarWidth = useGrooveStore((s) => s.setEditorSidebarWidth);
|
|
35
35
|
|
|
36
|
+
const projectDir = useGrooveStore((s) => s.projectDir);
|
|
36
37
|
const [rootDir, setRootDir] = useState('');
|
|
37
38
|
const [previewMode, setPreviewMode] = useState(false);
|
|
38
39
|
const [previewKey, setPreviewKey] = useState(0);
|
|
@@ -44,10 +45,10 @@ export default function EditorView() {
|
|
|
44
45
|
const startX = useRef(0);
|
|
45
46
|
const startW = useRef(0);
|
|
46
47
|
|
|
47
|
-
// Fetch root dir
|
|
48
|
+
// Fetch root dir on mount and when project directory changes
|
|
48
49
|
useEffect(() => {
|
|
49
50
|
api.get('/files/root').then((d) => setRootDir(d.root || '')).catch(() => {});
|
|
50
|
-
}, []);
|
|
51
|
+
}, [projectDir]);
|
|
51
52
|
|
|
52
53
|
// Reset preview mode when switching files
|
|
53
54
|
useEffect(() => { setPreviewMode(false); }, [activeFile]);
|
|
@@ -118,7 +119,7 @@ export default function EditorView() {
|
|
|
118
119
|
</div>
|
|
119
120
|
|
|
120
121
|
{/* Editor area */}
|
|
121
|
-
<div className="flex-1 flex flex-col min-w-0">
|
|
122
|
+
<div className="flex-1 flex flex-col min-w-0 bg-surface-0">
|
|
122
123
|
{/* Tab bar */}
|
|
123
124
|
<EditorTabs />
|
|
124
125
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groove-dev",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.73",
|
|
4
4
|
"description": "Open-source agent orchestration layer — the AI company OS. Local model agent engine (GGUF/Ollama/llama-server), HuggingFace model browser, MCP integrations (Slack, Gmail, Stripe, 15+), agent scheduling (cron), business roles (CMO, CFO, EA). GUI dashboard, multi-agent coordination, zero cold-start, infinite sessions. Works with Claude Code, Codex, Gemini CLI, Ollama, any local model.",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"author": "Groove Dev <hello@groovedev.ai> (https://groovedev.ai)",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
3
3
|
|
|
4
4
|
import express from 'express';
|
|
5
|
-
import { resolve, dirname, join, sep, relative } from 'path';
|
|
5
|
+
import { resolve, dirname, join, sep, relative, isAbsolute } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync, mkdirSync, unlinkSync, renameSync, rmSync, createReadStream, copyFileSync, realpathSync } from 'fs';
|
|
8
8
|
import { spawn, execFile, execFileSync } from 'child_process';
|
|
@@ -611,6 +611,7 @@ export function createApi(app, daemon) {
|
|
|
611
611
|
|
|
612
612
|
const proc = spawn('npm', ['install', '-g', pkg], {
|
|
613
613
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
614
|
+
shell: true,
|
|
614
615
|
env: { ...process.env, NODE_ENV: undefined },
|
|
615
616
|
});
|
|
616
617
|
|
|
@@ -719,6 +720,7 @@ export function createApi(app, daemon) {
|
|
|
719
720
|
|
|
720
721
|
const proc = spawn('codex', ['login'], {
|
|
721
722
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
723
|
+
shell: true,
|
|
722
724
|
});
|
|
723
725
|
let stdout = '';
|
|
724
726
|
let stderr = '';
|
|
@@ -763,7 +765,7 @@ export function createApi(app, daemon) {
|
|
|
763
765
|
if (customPath.length > 500) {
|
|
764
766
|
return res.status(400).json({ error: 'Path too long' });
|
|
765
767
|
}
|
|
766
|
-
if (!customPath
|
|
768
|
+
if (!isAbsolute(customPath)) {
|
|
767
769
|
return res.status(400).json({ error: 'Path must be absolute' });
|
|
768
770
|
}
|
|
769
771
|
|
|
@@ -830,6 +832,7 @@ export function createApi(app, daemon) {
|
|
|
830
832
|
encoding: 'utf8',
|
|
831
833
|
timeout: 5000,
|
|
832
834
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
835
|
+
shell: true,
|
|
833
836
|
}).trim();
|
|
834
837
|
} catch (err) {
|
|
835
838
|
version = null;
|
|
@@ -1033,7 +1036,7 @@ export function createApi(app, daemon) {
|
|
|
1033
1036
|
}
|
|
1034
1037
|
try {
|
|
1035
1038
|
daemon.setProjectDir(dirPath);
|
|
1036
|
-
|
|
1039
|
+
editorRootOverride = null;
|
|
1037
1040
|
res.json({ projectDir: daemon.projectDir, recentProjects: daemon.config.recentProjects || [] });
|
|
1038
1041
|
} catch (err) {
|
|
1039
1042
|
res.status(400).json({ error: err.message });
|
|
@@ -2660,12 +2663,13 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2660
2663
|
return LANG_MAP[ext] || 'text';
|
|
2661
2664
|
}
|
|
2662
2665
|
|
|
2663
|
-
const IGNORED_NAMES = new Set(['.
|
|
2666
|
+
const IGNORED_NAMES = new Set(['.DS_Store', '__pycache__']);
|
|
2664
2667
|
|
|
2665
|
-
// Editor root directory —
|
|
2666
|
-
|
|
2668
|
+
// Editor root directory — always tracks daemon.projectDir unless explicitly
|
|
2669
|
+
// overridden via POST /api/files/root. Reset on project-dir change.
|
|
2670
|
+
let editorRootOverride = null;
|
|
2667
2671
|
|
|
2668
|
-
function getEditorRoot() { return
|
|
2672
|
+
function getEditorRoot() { return editorRootOverride || daemon.projectDir; }
|
|
2669
2673
|
|
|
2670
2674
|
function validateFilePath(relPath, projectDir) {
|
|
2671
2675
|
if (!relPath || typeof relPath !== 'string') return { error: 'path is required' };
|
|
@@ -2689,13 +2693,12 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2689
2693
|
|
|
2690
2694
|
// Get/set the editor working directory
|
|
2691
2695
|
app.get('/api/files/root', (req, res) => {
|
|
2692
|
-
res.json({ root:
|
|
2696
|
+
res.json({ root: getEditorRoot() });
|
|
2693
2697
|
});
|
|
2694
2698
|
|
|
2695
2699
|
app.post('/api/files/root', (req, res) => {
|
|
2696
2700
|
const { root } = req.body || {};
|
|
2697
2701
|
if (!root || typeof root !== 'string') return res.status(400).json({ error: 'root path is required' });
|
|
2698
|
-
// Must be absolute and exist
|
|
2699
2702
|
if (!root.startsWith('/')) return res.status(400).json({ error: 'root must be an absolute path' });
|
|
2700
2703
|
if (root.includes('\0') || root.includes('..')) return res.status(400).json({ error: 'Invalid path' });
|
|
2701
2704
|
if (!existsSync(root)) return res.status(404).json({ error: 'Directory not found' });
|
|
@@ -2703,9 +2706,9 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2703
2706
|
const stat = statSync(root);
|
|
2704
2707
|
if (!stat.isDirectory()) return res.status(400).json({ error: 'Path is not a directory' });
|
|
2705
2708
|
} catch { return res.status(400).json({ error: 'Cannot access directory' }); }
|
|
2706
|
-
|
|
2709
|
+
editorRootOverride = root;
|
|
2707
2710
|
daemon.audit.log('editor.root.set', { root });
|
|
2708
|
-
res.json({ ok: true, root:
|
|
2711
|
+
res.json({ ok: true, root: getEditorRoot() });
|
|
2709
2712
|
});
|
|
2710
2713
|
|
|
2711
2714
|
// File tree — returns dirs + files for a given path
|
|
@@ -2730,18 +2733,22 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2730
2733
|
const raw = readdirSync(fullPath, { withFileTypes: true });
|
|
2731
2734
|
const entries = [];
|
|
2732
2735
|
|
|
2733
|
-
|
|
2734
|
-
|
|
2736
|
+
const dirs = raw.filter((e) => {
|
|
2737
|
+
if (e.name === '.DS_Store') return false;
|
|
2735
2738
|
if (e.isDirectory()) return true;
|
|
2736
|
-
if (e.isSymbolicLink()) {
|
|
2739
|
+
if (e.isSymbolicLink()) {
|
|
2740
|
+
try { return statSync(resolve(fullPath, e.name)).isDirectory(); }
|
|
2741
|
+
catch { return true; }
|
|
2742
|
+
}
|
|
2737
2743
|
return false;
|
|
2738
|
-
};
|
|
2739
|
-
const dirs = raw.filter((e) => isDir(e) && !IGNORED_NAMES.has(e.name) && !e.name.startsWith('.'))
|
|
2740
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
2744
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2741
2745
|
const files = raw.filter((e) => {
|
|
2742
|
-
if (e.name
|
|
2746
|
+
if (e.name === '.DS_Store') return false;
|
|
2743
2747
|
if (e.isFile()) return true;
|
|
2744
|
-
if (e.isSymbolicLink()) {
|
|
2748
|
+
if (e.isSymbolicLink()) {
|
|
2749
|
+
try { return statSync(resolve(fullPath, e.name)).isFile(); }
|
|
2750
|
+
catch { return false; }
|
|
2751
|
+
}
|
|
2745
2752
|
return false;
|
|
2746
2753
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
2747
2754
|
|
|
@@ -2751,7 +2758,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
2751
2758
|
let hasChildren = false;
|
|
2752
2759
|
try {
|
|
2753
2760
|
const children = readdirSync(childFull, { withFileTypes: true });
|
|
2754
|
-
hasChildren = children.some((c) =>
|
|
2761
|
+
hasChildren = children.some((c) => c.name !== '.DS_Store');
|
|
2755
2762
|
} catch { /* unreadable */ }
|
|
2756
2763
|
entries.push({ name: d.name, type: 'dir', path: childPath, hasChildren });
|
|
2757
2764
|
}
|
|
@@ -4077,6 +4084,7 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
4077
4084
|
|
|
4078
4085
|
const proc = spawn('npm', ['install', '-g', pkg], {
|
|
4079
4086
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
4087
|
+
shell: true,
|
|
4080
4088
|
env: { ...process.env, NODE_ENV: undefined },
|
|
4081
4089
|
});
|
|
4082
4090
|
|
|
@@ -5650,20 +5658,23 @@ Keep responses concise. Help them think, don't lecture them about the system the
|
|
|
5650
5658
|
} catch { /* non-fatal */ }
|
|
5651
5659
|
};
|
|
5652
5660
|
|
|
5653
|
-
// Serve GUI static files (built GUI) — no-cache headers
|
|
5661
|
+
// Serve GUI static files (built GUI) — no-cache headers prevent stale bundles on SSH reconnect
|
|
5654
5662
|
const guiPath = process.env.GROOVE_GUI_PATH || resolve(__dirname, '../../gui/dist');
|
|
5655
|
-
app.use(express.static(guiPath, {
|
|
5656
|
-
|
|
5657
|
-
|
|
5663
|
+
app.use(express.static(guiPath, {
|
|
5664
|
+
etag: false,
|
|
5665
|
+
maxAge: 0,
|
|
5666
|
+
lastModified: false,
|
|
5667
|
+
setHeaders: (res) => {
|
|
5658
5668
|
res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
5659
5669
|
res.set('Pragma', 'no-cache');
|
|
5660
|
-
}
|
|
5661
|
-
|
|
5662
|
-
});
|
|
5670
|
+
},
|
|
5671
|
+
}));
|
|
5663
5672
|
|
|
5664
5673
|
// SPA fallback
|
|
5665
5674
|
app.get('*', (req, res, next) => {
|
|
5666
5675
|
if (req.path.startsWith('/api/')) return next();
|
|
5676
|
+
res.set('Cache-Control', 'no-store, no-cache, must-revalidate');
|
|
5677
|
+
res.set('Pragma', 'no-cache');
|
|
5667
5678
|
res.sendFile(resolve(guiPath, 'index.html'), (err) => {
|
|
5668
5679
|
if (err) res.status(404).json({ error: 'GUI not built yet. Run: npm run build:gui' });
|
|
5669
5680
|
});
|
|
@@ -336,6 +336,7 @@ export class ClaudeCodeProvider extends Provider {
|
|
|
336
336
|
const child = cpSpawn('claude', ['auth', 'login', '--claudeai'], {
|
|
337
337
|
detached: true,
|
|
338
338
|
stdio: 'ignore',
|
|
339
|
+
shell: true,
|
|
339
340
|
});
|
|
340
341
|
child.unref();
|
|
341
342
|
return { pid: child.pid };
|
|
@@ -359,6 +360,7 @@ export class ClaudeCodeProvider extends Provider {
|
|
|
359
360
|
return new Promise((resolve) => {
|
|
360
361
|
const child = cpSpawn('claude', ['auth', 'login', '--claudeai'], {
|
|
361
362
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
363
|
+
shell: true,
|
|
362
364
|
});
|
|
363
365
|
let stdout = '';
|
|
364
366
|
let stderr = '';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
3
3
|
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
|
-
import { dirname as pathDirname } from 'path';
|
|
5
|
+
import { dirname as pathDirname, delimiter as pathDelimiter } from 'path';
|
|
6
6
|
import { ClaudeCodeProvider } from './claude-code.js';
|
|
7
7
|
import { CodexProvider } from './codex.js';
|
|
8
8
|
import { GeminiProvider } from './gemini.js';
|
|
@@ -26,10 +26,10 @@ function _augmentPathWithCustomPaths() {
|
|
|
26
26
|
for (const p of Object.values(_providerPaths)) {
|
|
27
27
|
if (p && typeof p === 'string') {
|
|
28
28
|
const dir = pathDirname(p);
|
|
29
|
-
if (dir && !cur.split(
|
|
29
|
+
if (dir && !cur.split(pathDelimiter).includes(dir)) dirs.push(dir);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
if (dirs.length) process.env.PATH = [...dirs, cur].join(
|
|
32
|
+
if (dirs.length) process.env.PATH = [...dirs, cur].join(pathDelimiter);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export function getProviderPath(id) {
|