groove-dev 0.27.74 → 0.27.75
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/CLAUDE.md +0 -7
- 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 +256 -4
- package/node_modules/@groove-dev/daemon/src/conversations.js +16 -0
- package/node_modules/@groove-dev/daemon/src/index.js +41 -1
- package/node_modules/@groove-dev/daemon/src/preview.js +18 -2
- package/node_modules/@groove-dev/daemon/src/process.js +6 -1
- package/node_modules/@groove-dev/daemon/src/providers/base.js +4 -0
- package/node_modules/@groove-dev/daemon/src/providers/codex.js +38 -0
- package/node_modules/@groove-dev/daemon/src/providers/grok.js +156 -0
- package/node_modules/@groove-dev/daemon/src/providers/index.js +5 -1
- package/node_modules/@groove-dev/daemon/src/providers/nano-banana.js +103 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CAT9SCJi.js +8620 -0
- package/node_modules/@groove-dev/gui/dist/assets/index-CVzz6zyb.css +1 -0
- 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/app.css +29 -0
- package/node_modules/@groove-dev/gui/src/app.jsx +2 -0
- package/node_modules/@groove-dev/gui/src/components/chat/chat-header.jsx +16 -5
- package/node_modules/@groove-dev/gui/src/components/chat/chat-input.jsx +40 -7
- package/node_modules/@groove-dev/gui/src/components/chat/chat-messages.jsx +149 -31
- package/node_modules/@groove-dev/gui/src/components/chat/chat-view.jsx +26 -2
- package/node_modules/@groove-dev/gui/src/components/chat/model-picker.jsx +105 -52
- package/node_modules/@groove-dev/gui/src/components/layout/activity-bar.jsx +5 -2
- package/node_modules/@groove-dev/gui/src/components/layout/welcome-splash.jsx +215 -88
- package/node_modules/@groove-dev/gui/src/components/preview/preview-toolbar.jsx +81 -0
- package/node_modules/@groove-dev/gui/src/components/preview/preview-workspace.jsx +263 -0
- package/node_modules/@groove-dev/gui/src/components/preview/screenshot-overlay.jsx +203 -0
- package/node_modules/@groove-dev/gui/src/components/ui/toast.jsx +6 -2
- package/node_modules/@groove-dev/gui/src/stores/groove.js +149 -9
- package/node_modules/@groove-dev/gui/src/views/preview.jsx +6 -0
- package/node_modules/@groove-dev/gui/src/views/settings.jsx +199 -114
- 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 +256 -4
- package/packages/daemon/src/conversations.js +16 -0
- package/packages/daemon/src/index.js +41 -1
- package/packages/daemon/src/preview.js +18 -2
- package/packages/daemon/src/process.js +6 -1
- package/packages/daemon/src/providers/base.js +4 -0
- package/packages/daemon/src/providers/codex.js +38 -0
- package/packages/daemon/src/providers/grok.js +156 -0
- package/packages/daemon/src/providers/index.js +5 -1
- package/packages/daemon/src/providers/nano-banana.js +103 -0
- package/packages/gui/dist/assets/index-CAT9SCJi.js +8620 -0
- package/packages/gui/dist/assets/index-CVzz6zyb.css +1 -0
- package/packages/gui/dist/index.html +2 -2
- package/packages/gui/package.json +1 -1
- package/packages/gui/src/app.css +29 -0
- package/packages/gui/src/app.jsx +2 -0
- package/packages/gui/src/components/chat/chat-header.jsx +16 -5
- package/packages/gui/src/components/chat/chat-input.jsx +40 -7
- package/packages/gui/src/components/chat/chat-messages.jsx +149 -31
- package/packages/gui/src/components/chat/chat-view.jsx +26 -2
- package/packages/gui/src/components/chat/model-picker.jsx +105 -52
- package/packages/gui/src/components/layout/activity-bar.jsx +5 -2
- package/packages/gui/src/components/layout/welcome-splash.jsx +215 -88
- package/packages/gui/src/components/preview/preview-toolbar.jsx +81 -0
- package/packages/gui/src/components/preview/preview-workspace.jsx +263 -0
- package/packages/gui/src/components/preview/screenshot-overlay.jsx +203 -0
- package/packages/gui/src/components/ui/toast.jsx +6 -2
- package/packages/gui/src/stores/groove.js +149 -9
- package/packages/gui/src/views/preview.jsx +6 -0
- package/packages/gui/src/views/settings.jsx +199 -114
- package/node_modules/@groove-dev/gui/dist/assets/index-DFP3r2yE.js +0 -8615
- package/node_modules/@groove-dev/gui/dist/assets/index-QR7lyguO.css +0 -1
- package/packages/gui/dist/assets/index-DFP3r2yE.js +0 -8615
- package/packages/gui/dist/assets/index-QR7lyguO.css +0 -1
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// GROOVE — xAI Grok Provider
|
|
2
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
3
|
+
|
|
4
|
+
import { Provider } from './base.js';
|
|
5
|
+
|
|
6
|
+
async function parseSSEStream(response, onEvent) {
|
|
7
|
+
const reader = response.body.getReader();
|
|
8
|
+
const decoder = new TextDecoder();
|
|
9
|
+
let buffer = '';
|
|
10
|
+
while (true) {
|
|
11
|
+
const { done, value } = await reader.read();
|
|
12
|
+
if (done) break;
|
|
13
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14
|
+
const lines = buffer.split('\n');
|
|
15
|
+
buffer = lines.pop();
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
if (line.startsWith('data: ')) {
|
|
18
|
+
const data = line.slice(6);
|
|
19
|
+
if (data === '[DONE]') { onEvent({ done: true }); return; }
|
|
20
|
+
try { onEvent(JSON.parse(data)); } catch { /* skip malformed */ }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class GrokProvider extends Provider {
|
|
27
|
+
static name = 'grok';
|
|
28
|
+
static displayName = 'xAI Grok';
|
|
29
|
+
static command = '';
|
|
30
|
+
static authType = 'api-key';
|
|
31
|
+
static envKey = 'XAI_API_KEY';
|
|
32
|
+
static models = [
|
|
33
|
+
{ id: 'grok-4', name: 'Grok 4', tier: 'heavy', maxContext: 131072, pricing: { input: 0.003, output: 0.015 } },
|
|
34
|
+
{ id: 'grok-4-1-fast', name: 'Grok 4.1 Fast', tier: 'medium', maxContext: 131072, pricing: { input: 0.0002, output: 0.0005 } },
|
|
35
|
+
{ id: 'grok-code-fast-1', name: 'Grok Code Fast', tier: 'medium', maxContext: 131072, pricing: { input: 0.0002, output: 0.0015 } },
|
|
36
|
+
{ id: 'grok-3', name: 'Grok 3', tier: 'heavy', maxContext: 131072, pricing: { input: 0.003, output: 0.015 } },
|
|
37
|
+
{ id: 'grok-3-mini', name: 'Grok 3 Mini', tier: 'light', maxContext: 131072, pricing: { input: 0.0003, output: 0.0005 } },
|
|
38
|
+
{ id: 'grok-imagine-image', name: 'Grok Imagine', tier: 'medium', type: 'image', pricing: { perImage: 0.07 } },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
static isInstalled() {
|
|
42
|
+
return true; // API-only, no CLI needed
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static installCommand() {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
buildSpawnCommand() {
|
|
50
|
+
return null; // No agent harness
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
buildHeadlessCommand() {
|
|
54
|
+
return null; // No CLI
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
switchModel() {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
parseOutput() {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static estimateCost(tokens, modelId) {
|
|
66
|
+
const model = GrokProvider.models.find((m) => m.id === modelId);
|
|
67
|
+
if (!model?.pricing) return 0;
|
|
68
|
+
const inputTokens = Math.round(tokens * 0.75);
|
|
69
|
+
const outputTokens = tokens - inputTokens;
|
|
70
|
+
return (inputTokens / 1000) * model.pricing.input + (outputTokens / 1000) * model.pricing.output;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
streamChat(messages, model, apiKey, onChunk, onDone, onError) {
|
|
74
|
+
if (!apiKey) return null;
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
let finished = false;
|
|
77
|
+
const finish = () => { if (!finished) { finished = true; onDone(); } };
|
|
78
|
+
fetch('https://api.x.ai/v1/chat/completions', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: {
|
|
81
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
},
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
model: model || 'grok-4-1-fast',
|
|
86
|
+
messages,
|
|
87
|
+
stream: true,
|
|
88
|
+
}),
|
|
89
|
+
signal: controller.signal,
|
|
90
|
+
}).then((res) => {
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
return res.text().then((t) => { throw new Error(`xAI API ${res.status}: ${t.slice(0, 200)}`); });
|
|
93
|
+
}
|
|
94
|
+
return parseSSEStream(res, (event) => {
|
|
95
|
+
if (event.done) { finish(); return; }
|
|
96
|
+
const content = event.choices?.[0]?.delta?.content;
|
|
97
|
+
if (content) onChunk(content);
|
|
98
|
+
});
|
|
99
|
+
}).then(() => {
|
|
100
|
+
finish();
|
|
101
|
+
}).catch((err) => {
|
|
102
|
+
if (err.name === 'AbortError') return;
|
|
103
|
+
onError(err);
|
|
104
|
+
});
|
|
105
|
+
return controller;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async generateImage(prompt, options = {}) {
|
|
109
|
+
const apiKey = options.apiKey;
|
|
110
|
+
if (!apiKey) throw new Error('XAI_API_KEY required for image generation');
|
|
111
|
+
|
|
112
|
+
const body = {
|
|
113
|
+
model: options.model || 'grok-imagine-image',
|
|
114
|
+
prompt,
|
|
115
|
+
n: 1,
|
|
116
|
+
};
|
|
117
|
+
if (options.size) body.size = options.size;
|
|
118
|
+
|
|
119
|
+
const res = await fetch('https://api.x.ai/v1/images/generations', {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
headers: {
|
|
122
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify(body),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (!res.ok) {
|
|
129
|
+
const text = await res.text();
|
|
130
|
+
throw new Error(`xAI Image API ${res.status}: ${text.slice(0, 200)}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const data = await res.json();
|
|
134
|
+
const image = data.data?.[0];
|
|
135
|
+
return {
|
|
136
|
+
url: image?.url || null,
|
|
137
|
+
b64_json: image?.b64_json || null,
|
|
138
|
+
model: body.model,
|
|
139
|
+
provider: 'grok',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static setupGuide() {
|
|
144
|
+
return {
|
|
145
|
+
installSteps: [],
|
|
146
|
+
authMethods: ['api-key'],
|
|
147
|
+
authInstructions: {
|
|
148
|
+
apiKeyHelp: 'Get your API key from console.x.ai',
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
static authMethods() {
|
|
154
|
+
return ['api-key'];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -7,6 +7,8 @@ import { ClaudeCodeProvider } from './claude-code.js';
|
|
|
7
7
|
import { CodexProvider } from './codex.js';
|
|
8
8
|
import { GeminiProvider } from './gemini.js';
|
|
9
9
|
import { OllamaProvider } from './ollama.js';
|
|
10
|
+
import { GrokProvider } from './grok.js';
|
|
11
|
+
import { NanaBananaProvider } from './nano-banana.js';
|
|
10
12
|
import { LocalProvider } from './local.js';
|
|
11
13
|
import { GrooveNetworkProvider } from './groove-network.js';
|
|
12
14
|
|
|
@@ -67,7 +69,9 @@ const providers = {
|
|
|
67
69
|
'claude-code': new ClaudeCodeProvider(),
|
|
68
70
|
'codex': new CodexProvider(),
|
|
69
71
|
'gemini': new GeminiProvider(),
|
|
72
|
+
'grok': new GrokProvider(),
|
|
70
73
|
'ollama': new OllamaProvider(),
|
|
74
|
+
'nano-banana': new NanaBananaProvider(),
|
|
71
75
|
'local': new LocalProvider(),
|
|
72
76
|
'groove-network': new GrooveNetworkProvider(),
|
|
73
77
|
};
|
|
@@ -93,7 +97,7 @@ export function getProvider(name) {
|
|
|
93
97
|
|
|
94
98
|
// Providers hidden from UI but kept for backward compatibility
|
|
95
99
|
// (existing agents with provider='ollama' still resolve via getProvider)
|
|
96
|
-
const HIDDEN_PROVIDERS = new Set(['ollama']);
|
|
100
|
+
const HIDDEN_PROVIDERS = new Set(['ollama', 'nano-banana']);
|
|
97
101
|
|
|
98
102
|
export function listProviders() {
|
|
99
103
|
return Object.entries(providers)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// GROOVE — Nano Banana Provider (Google Image Generation)
|
|
2
|
+
// FSL-1.1-Apache-2.0 — see LICENSE
|
|
3
|
+
//
|
|
4
|
+
// Nano Banana is Google's image generation family built on Gemini models:
|
|
5
|
+
// - Nano Banana 2 (Gemini 3.1 Flash Image) — fast generation
|
|
6
|
+
// - Nano Banana Pro (Gemini 3 Pro Image) — professional quality, up to 4K
|
|
7
|
+
// Uses the Gemini API with responseModalities: ["IMAGE"].
|
|
8
|
+
|
|
9
|
+
import { Provider } from './base.js';
|
|
10
|
+
|
|
11
|
+
export class NanaBananaProvider extends Provider {
|
|
12
|
+
static name = 'nano-banana';
|
|
13
|
+
static displayName = 'Nano Banana';
|
|
14
|
+
static command = '';
|
|
15
|
+
static authType = 'api-key';
|
|
16
|
+
static envKey = 'GEMINI_API_KEY';
|
|
17
|
+
static models = [
|
|
18
|
+
{ id: 'nano-banana-2', name: 'Nano Banana 2', tier: 'medium', type: 'image', geminiModel: 'gemini-2.0-flash-preview-image-generation', pricing: { perImage: 0.02 } },
|
|
19
|
+
{ id: 'nano-banana-pro', name: 'Nano Banana Pro', tier: 'heavy', type: 'image', geminiModel: 'gemini-2.0-flash-preview-image-generation', pricing: { perImage: 0.05 } },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
static isInstalled() {
|
|
23
|
+
return true; // API-only, shares GEMINI_API_KEY
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static installCommand() {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
buildSpawnCommand() {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
buildHeadlessCommand() {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
switchModel() {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
parseOutput() {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async generateImage(prompt, options = {}) {
|
|
47
|
+
const apiKey = options.apiKey;
|
|
48
|
+
if (!apiKey) throw new Error('GEMINI_API_KEY required for Nano Banana image generation');
|
|
49
|
+
|
|
50
|
+
const modelEntry = NanaBananaProvider.models.find((m) => m.id === (options.model || 'nano-banana-2'));
|
|
51
|
+
const geminiModel = modelEntry?.geminiModel || 'gemini-2.0-flash-preview-image-generation';
|
|
52
|
+
|
|
53
|
+
const res = await fetch(
|
|
54
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${geminiModel}:generateContent?key=${apiKey}`,
|
|
55
|
+
{
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
contents: [{ parts: [{ text: prompt }] }],
|
|
60
|
+
generationConfig: {
|
|
61
|
+
responseModalities: ['TEXT', 'IMAGE'],
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const text = await res.text();
|
|
69
|
+
throw new Error(`Nano Banana API ${res.status}: ${text.slice(0, 200)}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await res.json();
|
|
73
|
+
const parts = data.candidates?.[0]?.content?.parts || [];
|
|
74
|
+
const imagePart = parts.find((p) => p.inlineData);
|
|
75
|
+
|
|
76
|
+
if (!imagePart) {
|
|
77
|
+
const textPart = parts.find((p) => p.text);
|
|
78
|
+
throw new Error(textPart?.text || 'No image generated');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
url: null,
|
|
83
|
+
b64_json: imagePart.inlineData.data,
|
|
84
|
+
mimeType: imagePart.inlineData.mimeType || 'image/png',
|
|
85
|
+
model: options.model || 'nano-banana-2',
|
|
86
|
+
provider: 'nano-banana',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static setupGuide() {
|
|
91
|
+
return {
|
|
92
|
+
installSteps: [],
|
|
93
|
+
authMethods: ['api-key'],
|
|
94
|
+
authInstructions: {
|
|
95
|
+
apiKeyHelp: 'Uses your Gemini API key — get one at aistudio.google.com',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static authMethods() {
|
|
101
|
+
return ['api-key'];
|
|
102
|
+
}
|
|
103
|
+
}
|