@dubeyvishal/orbital-cli 1.6.13 → 1.6.14
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/package.json +4 -2
- package/server/src/cli/ai/googleService.js +80 -30
- package/server/src/cli/chat/chat-with-ai-agent.js +40 -20
- package/server/src/cli/chat/chat-with-ai-tools.js +108 -46
- package/server/src/cli/chat/chat-with-ai.js +232 -197
- package/server/src/cli/commands/ai/wakeUp.js +139 -78
- package/server/src/cli/commands/auth/login.js +1 -7
- package/server/src/cli/commands/config/setkey.js +57 -6
- package/server/src/cli/main.js +4 -1
- package/server/src/config/aiConfig.js +159 -0
- package/server/src/config/googleConfig.js +1 -1
- package/server/src/config/toolConfig.js +25 -44
- package/server/src/controllers/aiController.js +10 -6
- package/server/src/lib/credentialStore.js +47 -14
- package/server/src/lib/orbitalConfig.js +144 -38
- package/server/src/service/aiService.js +5 -5
|
@@ -7,7 +7,7 @@ const chatService = new ChatService();
|
|
|
7
7
|
|
|
8
8
|
export const respond = async (req, res, next) => {
|
|
9
9
|
try {
|
|
10
|
-
const { conversationId, mode, toolIds } = req.body || {};
|
|
10
|
+
const { conversationId, mode, toolIds, provider, model } = req.body || {};
|
|
11
11
|
|
|
12
12
|
if (!conversationId) {
|
|
13
13
|
return res.status(400).json({ error: "conversationId is required" });
|
|
@@ -16,16 +16,18 @@ export const respond = async (req, res, next) => {
|
|
|
16
16
|
const messages = await chatService.getMessages(conversationId);
|
|
17
17
|
const aiMessages = chatService.formatMessageForAI(messages);
|
|
18
18
|
|
|
19
|
+
const modelConfig = provider || model ? { provider, model } : null;
|
|
20
|
+
const aiService = getAIService(modelConfig);
|
|
21
|
+
|
|
19
22
|
let tools;
|
|
20
23
|
if (mode === "tool") {
|
|
21
24
|
resetTools();
|
|
22
25
|
if (Array.isArray(toolIds)) {
|
|
23
26
|
enableTools(toolIds);
|
|
24
27
|
}
|
|
25
|
-
tools = getEnabledTools();
|
|
28
|
+
tools = getEnabledTools(aiService.provider);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
const aiService = getAIService();
|
|
29
31
|
const result = await aiService.sendMessage(aiMessages, null, tools);
|
|
30
32
|
|
|
31
33
|
await chatService.addMessage(conversationId, "assistant", result.content);
|
|
@@ -34,6 +36,7 @@ export const respond = async (req, res, next) => {
|
|
|
34
36
|
content: result.content,
|
|
35
37
|
toolCalls: result.toolCalls || [],
|
|
36
38
|
toolResults: result.toolResults || [],
|
|
39
|
+
model: aiService.getDisplayName(),
|
|
37
40
|
});
|
|
38
41
|
} catch (error) {
|
|
39
42
|
return next(error);
|
|
@@ -42,16 +45,17 @@ export const respond = async (req, res, next) => {
|
|
|
42
45
|
|
|
43
46
|
export const generateAgentPlan = async (req, res, next) => {
|
|
44
47
|
try {
|
|
45
|
-
const { description } = req.body || {};
|
|
48
|
+
const { description, provider, model } = req.body || {};
|
|
46
49
|
|
|
47
50
|
if (!description || typeof description !== "string") {
|
|
48
51
|
return res.status(400).json({ error: "description is required" });
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
const
|
|
54
|
+
const modelConfig = provider || model ? { provider, model } : null;
|
|
55
|
+
const aiService = getAIService(modelConfig);
|
|
52
56
|
const application = await generateApplicationPlan(description, aiService);
|
|
53
57
|
|
|
54
|
-
return res.json({ application });
|
|
58
|
+
return res.json({ application, model: aiService.getDisplayName() });
|
|
55
59
|
} catch (error) {
|
|
56
60
|
return next(error);
|
|
57
61
|
}
|
|
@@ -16,32 +16,65 @@ const loadKeytar = async () => {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
export const getCredentialServiceName = () => ORBITAL_KEYTAR_SERVICE;
|
|
19
|
-
export const getApiKeyAccountName = () =>
|
|
19
|
+
export const getApiKeyAccountName = (provider = "gemini") => {
|
|
20
|
+
const p = (provider || "gemini").toLowerCase().trim();
|
|
21
|
+
if (p === "google" || p === "gemini") return "api-key-gemini";
|
|
22
|
+
if (p === "openai") return "api-key-openai";
|
|
23
|
+
if (p === "xai" || p === "grok") return "api-key-grok";
|
|
24
|
+
return `api-key-${p}`;
|
|
25
|
+
};
|
|
20
26
|
|
|
21
|
-
export const getStoredApiKey = async () => {
|
|
27
|
+
export const getStoredApiKey = async (provider = "gemini") => {
|
|
22
28
|
const keytar = await loadKeytar();
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
const account = getApiKeyAccountName(provider);
|
|
30
|
+
let value = await keytar.getPassword(ORBITAL_KEYTAR_SERVICE, account);
|
|
31
|
+
|
|
32
|
+
// Backward-compatibility for Gemini: fall back to legacy ORBITAL_API_KEY_ACCOUNT
|
|
33
|
+
const p = (provider || "gemini").toLowerCase().trim();
|
|
34
|
+
if (!value && (p === "gemini" || p === "google")) {
|
|
35
|
+
value = await keytar.getPassword(
|
|
36
|
+
ORBITAL_KEYTAR_SERVICE,
|
|
37
|
+
ORBITAL_API_KEY_ACCOUNT
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
27
41
|
return typeof value === "string" ? value.trim() : "";
|
|
28
42
|
};
|
|
29
43
|
|
|
30
|
-
export const storeApiKey = async (apiKey) => {
|
|
44
|
+
export const storeApiKey = async (apiKey, provider = "gemini") => {
|
|
31
45
|
const trimmed = typeof apiKey === "string" ? apiKey.trim() : "";
|
|
32
46
|
if (!trimmed) throw new Error("API key is required");
|
|
33
47
|
|
|
34
48
|
const keytar = await loadKeytar();
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
49
|
+
const account = getApiKeyAccountName(provider);
|
|
50
|
+
await keytar.setPassword(ORBITAL_KEYTAR_SERVICE, account, trimmed);
|
|
51
|
+
|
|
52
|
+
// For Gemini, also keep legacy account updated for backward compatibility
|
|
53
|
+
const p = (provider || "gemini").toLowerCase().trim();
|
|
54
|
+
if (p === "gemini" || p === "google") {
|
|
55
|
+
await keytar.setPassword(
|
|
56
|
+
ORBITAL_KEYTAR_SERVICE,
|
|
57
|
+
ORBITAL_API_KEY_ACCOUNT,
|
|
58
|
+
trimmed
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
40
62
|
return true;
|
|
41
63
|
};
|
|
42
64
|
|
|
43
|
-
export const deleteStoredApiKey = async () => {
|
|
65
|
+
export const deleteStoredApiKey = async (provider = "gemini") => {
|
|
44
66
|
const keytar = await loadKeytar();
|
|
45
|
-
|
|
67
|
+
const account = getApiKeyAccountName(provider);
|
|
68
|
+
await keytar.deletePassword(ORBITAL_KEYTAR_SERVICE, account);
|
|
69
|
+
|
|
70
|
+
const p = (provider || "gemini").toLowerCase().trim();
|
|
71
|
+
if (p === "gemini" || p === "google") {
|
|
72
|
+
await keytar.deletePassword(
|
|
73
|
+
ORBITAL_KEYTAR_SERVICE,
|
|
74
|
+
ORBITAL_API_KEY_ACCOUNT
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
46
78
|
return true;
|
|
47
79
|
};
|
|
80
|
+
|
|
@@ -86,10 +86,73 @@ export const updateOrbitalConfig = async (patch = {}) => {
|
|
|
86
86
|
return nextConfig;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
// --- Model Preference Persistence ---
|
|
90
|
+
|
|
91
|
+
export const getSelectedModel = async () => {
|
|
92
|
+
const cfg = await readOrbitalConfig();
|
|
93
|
+
let model = cfg.selectedModel || "gemini-2.5-flash";
|
|
94
|
+
if (model.includes("gemini-2.0")) model = "gemini-2.5-flash";
|
|
95
|
+
return {
|
|
96
|
+
provider: cfg.selectedProvider || "gemini",
|
|
97
|
+
model,
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const getSelectedModelSync = () => {
|
|
102
|
+
const cfg = readOrbitalConfigSync();
|
|
103
|
+
let model = cfg.selectedModel || "gemini-2.5-flash";
|
|
104
|
+
if (model.includes("gemini-2.0")) model = "gemini-2.5-flash";
|
|
105
|
+
return {
|
|
106
|
+
provider: cfg.selectedProvider || "gemini",
|
|
107
|
+
model,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export const saveSelectedModel = async ({ provider, model }) => {
|
|
112
|
+
return await updateOrbitalConfig({
|
|
113
|
+
selectedProvider: provider,
|
|
114
|
+
selectedModel: model,
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// --- Multi-Provider API Key Management ---
|
|
119
|
+
|
|
120
|
+
export const normalizeProviderName = (provider = "gemini") => {
|
|
121
|
+
const p = (provider || "gemini").toLowerCase().trim();
|
|
122
|
+
if (p === "google" || p === "gemini") return "gemini";
|
|
123
|
+
if (p === "openai") return "openai";
|
|
124
|
+
if (p === "xai" || p === "grok") return "grok";
|
|
125
|
+
return p;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const getApiKeyFromEnvSync = (provider = "gemini") => {
|
|
129
|
+
const norm = normalizeProviderName(provider);
|
|
130
|
+
if (norm === "gemini") {
|
|
131
|
+
return (
|
|
132
|
+
(typeof process.env.GOOGLE_GENERATIVE_AI_API_KEY === "string" &&
|
|
133
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY.trim()) ||
|
|
134
|
+
(typeof process.env.GEMINI_API_KEY === "string" &&
|
|
135
|
+
process.env.GEMINI_API_KEY.trim()) ||
|
|
136
|
+
""
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (norm === "openai") {
|
|
140
|
+
return (
|
|
141
|
+
(typeof process.env.OPENAI_API_KEY === "string" &&
|
|
142
|
+
process.env.OPENAI_API_KEY.trim()) ||
|
|
143
|
+
""
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
if (norm === "grok") {
|
|
147
|
+
return (
|
|
148
|
+
(typeof process.env.XAI_API_KEY === "string" &&
|
|
149
|
+
process.env.XAI_API_KEY.trim()) ||
|
|
150
|
+
(typeof process.env.GROK_API_KEY === "string" &&
|
|
151
|
+
process.env.GROK_API_KEY.trim()) ||
|
|
152
|
+
""
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return "";
|
|
93
156
|
};
|
|
94
157
|
|
|
95
158
|
const getLegacyGeminiApiKeyFromConfigSync = () => {
|
|
@@ -113,80 +176,123 @@ const removeLegacyGeminiApiKeyFromConfig = async () => {
|
|
|
113
176
|
return true;
|
|
114
177
|
};
|
|
115
178
|
|
|
116
|
-
export const
|
|
117
|
-
const
|
|
179
|
+
export const hydrateApiKeyEnv = async (provider = "gemini") => {
|
|
180
|
+
const norm = normalizeProviderName(provider);
|
|
181
|
+
const already = getApiKeyFromEnvSync(norm);
|
|
118
182
|
if (already) return already;
|
|
119
183
|
|
|
120
|
-
//
|
|
184
|
+
// OS credential manager via keytar
|
|
121
185
|
try {
|
|
122
|
-
const fromKeytar = await getStoredApiKey();
|
|
186
|
+
const fromKeytar = await getStoredApiKey(norm);
|
|
123
187
|
if (fromKeytar) {
|
|
124
|
-
|
|
188
|
+
if (norm === "gemini") {
|
|
189
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = fromKeytar;
|
|
190
|
+
} else if (norm === "openai") {
|
|
191
|
+
process.env.OPENAI_API_KEY = fromKeytar;
|
|
192
|
+
} else if (norm === "grok") {
|
|
193
|
+
process.env.XAI_API_KEY = fromKeytar;
|
|
194
|
+
}
|
|
125
195
|
return fromKeytar;
|
|
126
196
|
}
|
|
127
197
|
} catch {
|
|
128
|
-
//
|
|
198
|
+
// Ignore keytar error; will be handled in requireApiKey
|
|
129
199
|
}
|
|
130
200
|
|
|
131
|
-
// One-time migration
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
201
|
+
// One-time migration for legacy Gemini key on disk
|
|
202
|
+
if (norm === "gemini") {
|
|
203
|
+
const legacy = getLegacyGeminiApiKeyFromConfigSync();
|
|
204
|
+
if (legacy) {
|
|
205
|
+
await storeApiKey(legacy, "gemini");
|
|
206
|
+
await removeLegacyGeminiApiKeyFromConfig().catch(() => {});
|
|
207
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = legacy;
|
|
208
|
+
return legacy;
|
|
209
|
+
}
|
|
139
210
|
}
|
|
140
211
|
|
|
141
212
|
return "";
|
|
142
213
|
};
|
|
143
214
|
|
|
144
|
-
export const
|
|
215
|
+
export const hydrateAllApiKeysEnv = async () => {
|
|
216
|
+
await Promise.all([
|
|
217
|
+
hydrateApiKeyEnv("gemini"),
|
|
218
|
+
hydrateApiKeyEnv("openai"),
|
|
219
|
+
hydrateApiKeyEnv("grok"),
|
|
220
|
+
]);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const getApiKeySync = (provider = "gemini") => {
|
|
224
|
+
return getApiKeyFromEnvSync(provider);
|
|
225
|
+
};
|
|
145
226
|
|
|
146
|
-
export const
|
|
147
|
-
const
|
|
227
|
+
export const getApiKey = async (provider = "gemini") => {
|
|
228
|
+
const norm = normalizeProviderName(provider);
|
|
229
|
+
const fromEnv = getApiKeyFromEnvSync(norm);
|
|
148
230
|
if (fromEnv) return fromEnv;
|
|
149
|
-
return await
|
|
231
|
+
return await hydrateApiKeyEnv(norm);
|
|
150
232
|
};
|
|
151
233
|
|
|
152
|
-
export const
|
|
234
|
+
export const hasApiKeySync = (provider = "gemini") => {
|
|
235
|
+
return Boolean(getApiKeyFromEnvSync(provider));
|
|
236
|
+
};
|
|
153
237
|
|
|
154
|
-
export const
|
|
155
|
-
const
|
|
238
|
+
export const requireApiKeySync = (provider = "gemini") => {
|
|
239
|
+
const norm = normalizeProviderName(provider);
|
|
240
|
+
const apiKey = getApiKeyFromEnvSync(norm);
|
|
156
241
|
if (!apiKey) {
|
|
242
|
+
const displayName =
|
|
243
|
+
norm === "gemini" ? "Gemini" : norm === "openai" ? "OpenAI" : "Grok (xAI)";
|
|
157
244
|
const err = new Error(
|
|
158
|
-
|
|
245
|
+
`${displayName} API key not set. Run: orbital set-key --provider ${norm} <API_KEY>`
|
|
159
246
|
);
|
|
160
|
-
err.code =
|
|
247
|
+
err.code = `ORBITAL_${norm.toUpperCase()}_API_KEY_NOT_SET`;
|
|
161
248
|
throw err;
|
|
162
249
|
}
|
|
163
250
|
return apiKey;
|
|
164
251
|
};
|
|
165
252
|
|
|
166
|
-
export const
|
|
167
|
-
const
|
|
253
|
+
export const requireApiKey = async (provider = "gemini") => {
|
|
254
|
+
const norm = normalizeProviderName(provider);
|
|
255
|
+
const apiKey = await getApiKey(norm);
|
|
168
256
|
if (!apiKey) {
|
|
257
|
+
const displayName =
|
|
258
|
+
norm === "gemini" ? "Gemini" : norm === "openai" ? "OpenAI" : "Grok (xAI)";
|
|
169
259
|
const err = new Error(
|
|
170
|
-
|
|
260
|
+
`${displayName} API key not set. Run: orbital set-key --provider ${norm} <API_KEY>`
|
|
171
261
|
);
|
|
172
|
-
err.code =
|
|
262
|
+
err.code = `ORBITAL_${norm.toUpperCase()}_API_KEY_NOT_SET`;
|
|
173
263
|
throw err;
|
|
174
264
|
}
|
|
175
265
|
return apiKey;
|
|
176
266
|
};
|
|
177
267
|
|
|
178
|
-
export const
|
|
268
|
+
export const setApiKey = async (provider = "gemini", apiKey) => {
|
|
269
|
+
const norm = normalizeProviderName(provider);
|
|
179
270
|
const trimmed = typeof apiKey === "string" ? apiKey.trim() : "";
|
|
180
271
|
if (!trimmed) throw new Error("API key is required");
|
|
181
272
|
|
|
182
|
-
await storeApiKey(trimmed);
|
|
183
|
-
|
|
184
|
-
|
|
273
|
+
await storeApiKey(trimmed, norm);
|
|
274
|
+
|
|
275
|
+
if (norm === "gemini") {
|
|
276
|
+
await removeLegacyGeminiApiKeyFromConfig().catch(() => {});
|
|
277
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = trimmed;
|
|
278
|
+
process.env.GEMINI_API_KEY = trimmed;
|
|
279
|
+
} else if (norm === "openai") {
|
|
280
|
+
process.env.OPENAI_API_KEY = trimmed;
|
|
281
|
+
} else if (norm === "grok") {
|
|
282
|
+
process.env.XAI_API_KEY = trimmed;
|
|
283
|
+
process.env.GROK_API_KEY = trimmed;
|
|
284
|
+
}
|
|
185
285
|
|
|
186
|
-
process.env.GOOGLE_GENERATIVE_AI_API_KEY = trimmed;
|
|
187
286
|
return true;
|
|
188
287
|
};
|
|
189
288
|
|
|
190
|
-
// Back-
|
|
191
|
-
export const requireGeminiApiKeyFromConfigSync = requireGeminiApiKeySync;
|
|
289
|
+
// --- Back-compatibility exports for Gemini ---
|
|
192
290
|
|
|
291
|
+
export const hydrateGeminiApiKeyEnv = () => hydrateApiKeyEnv("gemini");
|
|
292
|
+
export const getGeminiApiKeySync = () => getApiKeySync("gemini");
|
|
293
|
+
export const getGeminiApiKey = () => getApiKey("gemini");
|
|
294
|
+
export const hasGeminiApiKeySync = () => hasApiKeySync("gemini");
|
|
295
|
+
export const requireGeminiApiKeySync = () => requireApiKeySync("gemini");
|
|
296
|
+
export const requireGeminiApiKey = () => requireApiKey("gemini");
|
|
297
|
+
export const setGeminiApiKey = (apiKey) => setApiKey("gemini", apiKey);
|
|
298
|
+
export const requireGeminiApiKeyFromConfigSync = requireGeminiApiKeySync;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AIService } from "../cli/ai/googleService.js";
|
|
2
|
-
|
|
3
|
-
export const getAIService = () => {
|
|
4
|
-
return new AIService();
|
|
5
|
-
};
|
|
1
|
+
import { AIService } from "../cli/ai/googleService.js";
|
|
2
|
+
|
|
3
|
+
export const getAIService = (modelConfig = null) => {
|
|
4
|
+
return new AIService(modelConfig);
|
|
5
|
+
};
|