@dubeyvishal/orbital-cli 1.6.14 → 1.6.15
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 +3 -3
- package/package.json +2 -4
- package/server/src/cli/ai/googleService.js +30 -80
- package/server/src/cli/chat/chat-with-ai-agent.js +20 -40
- package/server/src/cli/chat/chat-with-ai-tools.js +46 -108
- package/server/src/cli/chat/chat-with-ai.js +197 -232
- package/server/src/cli/commands/ai/wakeUp.js +78 -139
- package/server/src/cli/commands/auth/login.js +7 -1
- package/server/src/cli/commands/config/setkey.js +6 -57
- package/server/src/cli/main.js +1 -4
- package/server/src/config/googleConfig.js +1 -1
- package/server/src/config/toolConfig.js +44 -25
- package/server/src/controllers/aiController.js +6 -10
- package/server/src/lib/credentialStore.js +14 -47
- package/server/src/lib/orbitalConfig.js +38 -144
- package/server/src/service/aiService.js +2 -2
- package/server/src/config/aiConfig.js +0 -159
|
@@ -86,73 +86,10 @@ export const updateOrbitalConfig = async (patch = {}) => {
|
|
|
86
86
|
return nextConfig;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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 "";
|
|
89
|
+
const getGeminiApiKeyFromEnvSync = () => {
|
|
90
|
+
return typeof process.env.GOOGLE_GENERATIVE_AI_API_KEY === "string"
|
|
91
|
+
? process.env.GOOGLE_GENERATIVE_AI_API_KEY.trim()
|
|
92
|
+
: "";
|
|
156
93
|
};
|
|
157
94
|
|
|
158
95
|
const getLegacyGeminiApiKeyFromConfigSync = () => {
|
|
@@ -176,123 +113,80 @@ const removeLegacyGeminiApiKeyFromConfig = async () => {
|
|
|
176
113
|
return true;
|
|
177
114
|
};
|
|
178
115
|
|
|
179
|
-
export const
|
|
180
|
-
const
|
|
181
|
-
const already = getApiKeyFromEnvSync(norm);
|
|
116
|
+
export const hydrateGeminiApiKeyEnv = async () => {
|
|
117
|
+
const already = getGeminiApiKeyFromEnvSync();
|
|
182
118
|
if (already) return already;
|
|
183
119
|
|
|
184
|
-
// OS credential manager via keytar
|
|
120
|
+
// Primary: OS credential manager via keytar.
|
|
185
121
|
try {
|
|
186
|
-
const fromKeytar = await getStoredApiKey(
|
|
122
|
+
const fromKeytar = await getStoredApiKey();
|
|
187
123
|
if (fromKeytar) {
|
|
188
|
-
|
|
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
|
-
}
|
|
124
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = fromKeytar;
|
|
195
125
|
return fromKeytar;
|
|
196
126
|
}
|
|
197
127
|
} catch {
|
|
198
|
-
//
|
|
128
|
+
// ignore here; requireGeminiApiKey will surface a helpful error
|
|
199
129
|
}
|
|
200
130
|
|
|
201
|
-
// One-time migration
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
131
|
+
// One-time migration: if the key exists in legacy ~/.orbital/config.json,
|
|
132
|
+
// move it into keytar and remove it from disk.
|
|
133
|
+
const legacy = getLegacyGeminiApiKeyFromConfigSync();
|
|
134
|
+
if (legacy) {
|
|
135
|
+
await storeApiKey(legacy);
|
|
136
|
+
await removeLegacyGeminiApiKeyFromConfig().catch(() => {});
|
|
137
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = legacy;
|
|
138
|
+
return legacy;
|
|
210
139
|
}
|
|
211
140
|
|
|
212
141
|
return "";
|
|
213
142
|
};
|
|
214
143
|
|
|
215
|
-
export const
|
|
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
|
-
};
|
|
144
|
+
export const getGeminiApiKeySync = () => getGeminiApiKeyFromEnvSync();
|
|
226
145
|
|
|
227
|
-
export const
|
|
228
|
-
const
|
|
229
|
-
const fromEnv = getApiKeyFromEnvSync(norm);
|
|
146
|
+
export const getGeminiApiKey = async () => {
|
|
147
|
+
const fromEnv = getGeminiApiKeyFromEnvSync();
|
|
230
148
|
if (fromEnv) return fromEnv;
|
|
231
|
-
return await
|
|
149
|
+
return await hydrateGeminiApiKeyEnv();
|
|
232
150
|
};
|
|
233
151
|
|
|
234
|
-
export const
|
|
235
|
-
return Boolean(getApiKeyFromEnvSync(provider));
|
|
236
|
-
};
|
|
152
|
+
export const hasGeminiApiKeySync = () => Boolean(getGeminiApiKeyFromEnvSync());
|
|
237
153
|
|
|
238
|
-
export const
|
|
239
|
-
const
|
|
240
|
-
const apiKey = getApiKeyFromEnvSync(norm);
|
|
154
|
+
export const requireGeminiApiKeySync = () => {
|
|
155
|
+
const apiKey = getGeminiApiKeyFromEnvSync();
|
|
241
156
|
if (!apiKey) {
|
|
242
|
-
const displayName =
|
|
243
|
-
norm === "gemini" ? "Gemini" : norm === "openai" ? "OpenAI" : "Grok (xAI)";
|
|
244
157
|
const err = new Error(
|
|
245
|
-
|
|
158
|
+
"Gemini API key not set. Run: orbital set-key <API_KEY>"
|
|
246
159
|
);
|
|
247
|
-
err.code =
|
|
160
|
+
err.code = "ORBITAL_GEMINI_API_KEY_NOT_SET";
|
|
248
161
|
throw err;
|
|
249
162
|
}
|
|
250
163
|
return apiKey;
|
|
251
164
|
};
|
|
252
165
|
|
|
253
|
-
export const
|
|
254
|
-
const
|
|
255
|
-
const apiKey = await getApiKey(norm);
|
|
166
|
+
export const requireGeminiApiKey = async () => {
|
|
167
|
+
const apiKey = await getGeminiApiKey();
|
|
256
168
|
if (!apiKey) {
|
|
257
|
-
const displayName =
|
|
258
|
-
norm === "gemini" ? "Gemini" : norm === "openai" ? "OpenAI" : "Grok (xAI)";
|
|
259
169
|
const err = new Error(
|
|
260
|
-
|
|
170
|
+
"Gemini API key not set. Run: orbital set-key <API_KEY>"
|
|
261
171
|
);
|
|
262
|
-
err.code =
|
|
172
|
+
err.code = "ORBITAL_GEMINI_API_KEY_NOT_SET";
|
|
263
173
|
throw err;
|
|
264
174
|
}
|
|
265
175
|
return apiKey;
|
|
266
176
|
};
|
|
267
177
|
|
|
268
|
-
export const
|
|
269
|
-
const norm = normalizeProviderName(provider);
|
|
178
|
+
export const setGeminiApiKey = async (apiKey) => {
|
|
270
179
|
const trimmed = typeof apiKey === "string" ? apiKey.trim() : "";
|
|
271
180
|
if (!trimmed) throw new Error("API key is required");
|
|
272
181
|
|
|
273
|
-
await storeApiKey(trimmed
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
}
|
|
182
|
+
await storeApiKey(trimmed);
|
|
183
|
+
// Ensure any legacy on-disk key is removed.
|
|
184
|
+
await removeLegacyGeminiApiKeyFromConfig().catch(() => {});
|
|
285
185
|
|
|
186
|
+
process.env.GOOGLE_GENERATIVE_AI_API_KEY = trimmed;
|
|
286
187
|
return true;
|
|
287
188
|
};
|
|
288
189
|
|
|
289
|
-
//
|
|
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);
|
|
190
|
+
// Back-compat exports (no longer reads from config specifically).
|
|
298
191
|
export const requireGeminiApiKeyFromConfigSync = requireGeminiApiKeySync;
|
|
192
|
+
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import "./env.js";
|
|
2
|
-
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
3
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
4
|
-
import { createXai } from "@ai-sdk/xai";
|
|
5
|
-
import { normalizeProviderName, requireApiKeySync } from "../lib/orbitalConfig.js";
|
|
6
|
-
|
|
7
|
-
export const AI_PROVIDERS = {
|
|
8
|
-
gemini: {
|
|
9
|
-
id: "gemini",
|
|
10
|
-
name: "Google Gemini",
|
|
11
|
-
defaultModel: "gemini-2.5-flash",
|
|
12
|
-
envKey: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
13
|
-
docsUrl: "https://aistudio.google.com/app/apikey",
|
|
14
|
-
models: [
|
|
15
|
-
{
|
|
16
|
-
id: "gemini-2.5-flash",
|
|
17
|
-
name: "Gemini 2.5 Flash",
|
|
18
|
-
hint: "Google (Fast & versatile - Recommended)",
|
|
19
|
-
isDefault: true,
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: "gemini-2.5-pro",
|
|
23
|
-
name: "Gemini 2.5 Pro",
|
|
24
|
-
hint: "Google (Complex reasoning & large context)",
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: "gemini-1.5-flash",
|
|
28
|
-
name: "Gemini 1.5 Flash",
|
|
29
|
-
hint: "Google (Lightweight & efficient)",
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: "gemini-1.5-pro",
|
|
33
|
-
name: "Gemini 1.5 Pro",
|
|
34
|
-
hint: "Google (Extended analysis)",
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
38
|
-
openai: {
|
|
39
|
-
id: "openai",
|
|
40
|
-
name: "OpenAI",
|
|
41
|
-
defaultModel: "gpt-4o",
|
|
42
|
-
envKey: "OPENAI_API_KEY",
|
|
43
|
-
docsUrl: "https://platform.openai.com/api-keys",
|
|
44
|
-
models: [
|
|
45
|
-
{
|
|
46
|
-
id: "gpt-4o",
|
|
47
|
-
name: "GPT-4o",
|
|
48
|
-
hint: "OpenAI (Flagship multimodal intelligence)",
|
|
49
|
-
isDefault: true,
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
id: "gpt-4o-mini",
|
|
53
|
-
name: "GPT-4o Mini",
|
|
54
|
-
hint: "OpenAI (Fast & cost-effective)",
|
|
55
|
-
},
|
|
56
|
-
],
|
|
57
|
-
},
|
|
58
|
-
grok: {
|
|
59
|
-
id: "grok",
|
|
60
|
-
name: "Grok (xAI)",
|
|
61
|
-
defaultModel: "grok-2-latest",
|
|
62
|
-
envKey: "XAI_API_KEY",
|
|
63
|
-
docsUrl: "https://console.x.ai/",
|
|
64
|
-
models: [
|
|
65
|
-
{
|
|
66
|
-
id: "grok-2-latest",
|
|
67
|
-
name: "Grok 2",
|
|
68
|
-
hint: "xAI (Frontier model with advanced reasoning)",
|
|
69
|
-
isDefault: true,
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: "grok-beta",
|
|
73
|
-
name: "Grok Beta",
|
|
74
|
-
hint: "xAI (Experimental release)",
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Returns options list suitable for @clack/prompts select()
|
|
82
|
-
*/
|
|
83
|
-
export const getModelSelectOptions = () => {
|
|
84
|
-
const options = [];
|
|
85
|
-
for (const provider of Object.values(AI_PROVIDERS)) {
|
|
86
|
-
for (const model of provider.models) {
|
|
87
|
-
options.push({
|
|
88
|
-
value: `${provider.id}:${model.id}`,
|
|
89
|
-
label: `${provider.name} - ${model.name}`,
|
|
90
|
-
hint: model.hint,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return options;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Parse a compound value like "openai:gpt-4o" or separate provider/model inputs
|
|
99
|
-
*/
|
|
100
|
-
export const parseModelChoice = (choice) => {
|
|
101
|
-
if (!choice) return { provider: "gemini", model: "gemini-2.5-flash" };
|
|
102
|
-
if (typeof choice === "object") {
|
|
103
|
-
const provider = normalizeProviderName(choice.provider || "gemini");
|
|
104
|
-
const defaultModel = AI_PROVIDERS[provider]?.defaultModel || "gemini-2.5-flash";
|
|
105
|
-
let model = choice.model || defaultModel;
|
|
106
|
-
if (model.includes("gemini-2.0")) model = "gemini-2.5-flash";
|
|
107
|
-
return {
|
|
108
|
-
provider,
|
|
109
|
-
model,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (typeof choice === "string" && choice.includes(":")) {
|
|
114
|
-
const [p, m] = choice.split(":");
|
|
115
|
-
const provider = normalizeProviderName(p);
|
|
116
|
-
const model = m.includes("gemini-2.0") ? "gemini-2.5-flash" : m;
|
|
117
|
-
return { provider, model };
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// If only provider name is given
|
|
121
|
-
const provider = normalizeProviderName(choice);
|
|
122
|
-
return {
|
|
123
|
-
provider,
|
|
124
|
-
model: AI_PROVIDERS[provider]?.defaultModel || choice,
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
export const getModelDisplayName = (provider, modelId) => {
|
|
130
|
-
const norm = normalizeProviderName(provider);
|
|
131
|
-
const provInfo = AI_PROVIDERS[norm];
|
|
132
|
-
if (!provInfo) return `${provider} (${modelId})`;
|
|
133
|
-
const modelInfo = provInfo.models.find((m) => m.id === modelId);
|
|
134
|
-
return modelInfo ? `${provInfo.name} (${modelInfo.name})` : `${provInfo.name} (${modelId})`;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Creates a language model instance for Vercel AI SDK
|
|
139
|
-
*/
|
|
140
|
-
export const createModelInstance = (provider, modelName, apiKey = null) => {
|
|
141
|
-
const norm = normalizeProviderName(provider);
|
|
142
|
-
const key = apiKey || requireApiKeySync(norm);
|
|
143
|
-
|
|
144
|
-
switch (norm) {
|
|
145
|
-
case "openai": {
|
|
146
|
-
const openai = createOpenAI({ apiKey: key });
|
|
147
|
-
return openai(modelName);
|
|
148
|
-
}
|
|
149
|
-
case "grok": {
|
|
150
|
-
const xai = createXai({ apiKey: key });
|
|
151
|
-
return xai(modelName);
|
|
152
|
-
}
|
|
153
|
-
case "gemini":
|
|
154
|
-
default: {
|
|
155
|
-
const google = createGoogleGenerativeAI({ apiKey: key });
|
|
156
|
-
return google(modelName);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
};
|