@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.
@@ -1,84 +1,145 @@
1
1
  import chalk from "chalk";
2
- import {Command} from "commander";
3
- import yoctoSpinner from "yocto-spinner";
4
- import {getStoredToken} from "../../../lib/token.js"
5
- import {select} from "@clack/prompts";
6
- import {startChat} from "../../../cli/chat/chat-with-ai.js";
7
- import {startToolChat} from "../../../cli/chat/chat-with-ai-tools.js";
8
- import {startAgentChat} from "../../../cli/chat/chat-with-ai-agent.js";
2
+ import { Command } from "commander";
3
+ import yoctoSpinner from "yocto-spinner";
4
+ import { getStoredToken } from "../../../lib/token.js";
5
+ import { select, password, isCancel, cancel } from "@clack/prompts";
6
+ import { startChat } from "../../../cli/chat/chat-with-ai.js";
7
+ import { startToolChat } from "../../../cli/chat/chat-with-ai-tools.js";
8
+ import { startAgentChat } from "../../../cli/chat/chat-with-ai-agent.js";
9
9
  import { apiRequestSafe } from "../../utils/apiClient.js";
10
- import { requireGeminiApiKey } from "../../../lib/orbitalConfig.js";
10
+ import {
11
+ getApiKey,
12
+ setApiKey,
13
+ getSelectedModel,
14
+ saveSelectedModel,
15
+ } from "../../../lib/orbitalConfig.js";
16
+ import {
17
+ AI_PROVIDERS,
18
+ getModelSelectOptions,
19
+ parseModelChoice,
20
+ getModelDisplayName,
21
+ } from "../../../config/aiConfig.js";
11
22
 
23
+ const wakeUpAction = async () => {
24
+ const token = await getStoredToken();
25
+ if (!token?.access_token) {
26
+ console.log(chalk.red("Not Authenticated. Please run 'orbital login' first."));
27
+ return;
28
+ }
12
29
 
13
- const wakeUpAction = async()=>{
14
- try {
15
- await requireGeminiApiKey();
16
- } catch {
17
- console.log(chalk.red("Gemini API key not set. Run: orbital set-key <API_KEY>"));
18
- return;
30
+ const spinner = yoctoSpinner({ text: "Fetching user information..." });
31
+ spinner.start();
32
+
33
+ let user;
34
+ try {
35
+ const result = await apiRequestSafe("/api/cli/me");
36
+ user = result?.user;
37
+ } finally {
38
+ spinner.stop();
39
+ }
40
+
41
+ if (!user) {
42
+ console.log(chalk.red("User not found. Please log in again with 'orbital login'."));
43
+ return;
44
+ }
45
+
46
+ console.log(chalk.green(`Welcome back, ${user.name}! \n`));
47
+
48
+ // 1. Ask user to select the AI model FIRST
49
+ const savedModel = await getSelectedModel();
50
+ const modelOptions = getModelSelectOptions();
51
+ const defaultModelValue = `${savedModel.provider}:${savedModel.model}`;
52
+
53
+ const selectedModelChoice = await select({
54
+ message: "Select an AI Model:",
55
+ options: modelOptions,
56
+ initialValue: modelOptions.some((o) => o.value === defaultModelValue)
57
+ ? defaultModelValue
58
+ : undefined,
59
+ });
60
+
61
+ if (isCancel(selectedModelChoice)) {
62
+ cancel("Model selection cancelled.");
63
+ return;
64
+ }
65
+
66
+ const { provider, model } = parseModelChoice(selectedModelChoice);
67
+ await saveSelectedModel({ provider, model });
68
+
69
+ // 2. Ensure API key is configured for the chosen model/provider
70
+ let apiKey = await getApiKey(provider);
71
+ if (!apiKey) {
72
+ const provInfo = AI_PROVIDERS[provider];
73
+ console.log(
74
+ chalk.yellow(
75
+ `\n${provInfo?.name || provider} API key is required. (Obtain one at: ${provInfo?.docsUrl || "provider portal"})`
76
+ )
77
+ );
78
+
79
+ const enteredKey = await password({
80
+ message: `Enter your ${provInfo?.name || provider} API key:`,
81
+ validate(value) {
82
+ if (!value || !value.trim()) return "API key cannot be empty";
83
+ },
84
+ });
85
+
86
+ if (isCancel(enteredKey)) {
87
+ cancel("Operation cancelled.");
88
+ return;
19
89
  }
20
90
 
21
- const token = await getStoredToken();
22
- if(!token?.access_token){
23
- console.log(chalk.red("Not Authenticated. please login"))
24
- return ;
25
- }
26
-
27
- const spinner = yoctoSpinner({text: "Fetching user information ..."})
28
- spinner.start()
29
-
30
- let user;
31
- try{
32
- const result = await apiRequestSafe("/api/cli/me");
33
- user = result?.user;
34
- }
35
- finally{
36
- spinner.stop();
37
- }
38
-
39
- if(!user){
40
- console.log(chalk.red("User not found."))
41
- return ;
42
- }
43
-
44
- console.log(chalk.green(`Welcome back, ${user.name}! \n`));
45
-
46
- const choice = await select({
47
- message : "Select an Option",
48
- options : [
49
- {
50
- value : "chat",
51
- label: "Chat",
52
- hint : "Simple chat with AI"
53
- },
54
- {
55
- value : "tool",
56
- label: "Tool Calling",
57
- hint : "Chat with tools (Google Search , Code Execution"
58
- },
59
- {
60
- value : "agent",
61
- label: "Agentic Mode",
62
- hint : "Advanced AI Agent"
63
- },
64
- ]
65
- });
66
-
67
- switch(choice){
68
- case "chat":
69
- await startChat("chat");
70
- break ;
71
- case "tool":
72
- await startToolChat();
73
- break ;
74
- case "agent":
75
- await startAgentChat();
76
- break ;
77
- }
78
- }
79
-
80
- export const wakeUp = new Command("wakeup").
81
- description("Wake up the AI").
82
- alias("wake-up").
83
- alias("wakup").
84
- action(wakeUpAction)
91
+ await setApiKey(provider, enteredKey);
92
+ console.log(chalk.green(`✔ ${provInfo?.name || provider} API key saved securely.\n`));
93
+ }
94
+
95
+ console.log(
96
+ chalk.cyan(`Active AI Model: ${chalk.bold(getModelDisplayName(provider, model))}\n`)
97
+ );
98
+
99
+ // 3. Mode selection
100
+ const choice = await select({
101
+ message: "Select an Option",
102
+ options: [
103
+ {
104
+ value: "chat",
105
+ label: "Chat",
106
+ hint: "Simple chat with AI",
107
+ },
108
+ {
109
+ value: "tool",
110
+ label: "Tool Calling",
111
+ hint: "Chat with tools and code execution",
112
+ },
113
+ {
114
+ value: "agent",
115
+ label: "Agentic Mode",
116
+ hint: "Fullstack application generation agent",
117
+ },
118
+ ],
119
+ });
120
+
121
+ if (isCancel(choice)) {
122
+ cancel("Operation cancelled.");
123
+ return;
124
+ }
125
+
126
+ const modelConfig = { provider, model };
127
+
128
+ switch (choice) {
129
+ case "chat":
130
+ await startChat("chat", null, modelConfig);
131
+ break;
132
+ case "tool":
133
+ await startToolChat(modelConfig);
134
+ break;
135
+ case "agent":
136
+ await startAgentChat(modelConfig);
137
+ break;
138
+ }
139
+ };
140
+
141
+ export const wakeUp = new Command("wakeup")
142
+ .description("Wake up the AI")
143
+ .alias("wake-up")
144
+ .alias("wakup")
145
+ .action(wakeUpAction);
@@ -11,7 +11,6 @@ import { fileURLToPath } from "url";
11
11
  import { getStoredToken, isTokenExpired, storeToken ,TOKEN_FILE } from "../../../lib/token.js";
12
12
  import { API_BASE } from "../../../config/api.js";
13
13
  import { apiRequestSafe } from "../../utils/apiClient.js";
14
- import { requireGeminiApiKey } from "../../../lib/orbitalConfig.js";
15
14
 
16
15
 
17
16
  const __filename = fileURLToPath(import.meta.url);
@@ -93,12 +92,7 @@ const resolveClientId = async (cliClientId) => {
93
92
 
94
93
 
95
94
  export const loginAction = async (cmdOptions) => {
96
- try {
97
- await requireGeminiApiKey();
98
- } catch {
99
- console.log(chalk.red("Gemini API key not set. Run: orbital set-key <API_KEY>"));
100
- process.exit(1);
101
- }
95
+
102
96
 
103
97
  const schema = z.object({
104
98
  serverUrl: z.string().optional(),
@@ -1,12 +1,58 @@
1
1
  import { Command } from "commander";
2
2
  import chalk from "chalk";
3
- import { setGeminiApiKey } from "../../../lib/orbitalConfig.js";
3
+ import { select, password, isCancel, cancel } from "@clack/prompts";
4
+ import { setApiKey, normalizeProviderName } from "../../../lib/orbitalConfig.js";
4
5
  import { getCredentialServiceName } from "../../../lib/credentialStore.js";
6
+ import { AI_PROVIDERS } from "../../../config/aiConfig.js";
5
7
 
6
- const setKeyAction = async (apiKey) => {
8
+ const setKeyAction = async (apiKeyArg, cmdOptions) => {
7
9
  try {
8
- await setGeminiApiKey(apiKey);
9
- console.log(chalk.green("Gemini API key saved."));
10
+ let provider = cmdOptions.provider;
11
+ let apiKey = apiKeyArg;
12
+
13
+ // Interactive mode if key was not supplied via command line argument
14
+ if (!apiKey) {
15
+ if (!provider) {
16
+ const provChoice = await select({
17
+ message: "Select AI Provider to configure API key for:",
18
+ options: [
19
+ { value: "gemini", label: "Google Gemini", hint: "AI Studio API key" },
20
+ { value: "openai", label: "OpenAI", hint: "platform.openai.com key" },
21
+ { value: "grok", label: "Grok (xAI)", hint: "console.x.ai key" },
22
+ ],
23
+ });
24
+
25
+ if (isCancel(provChoice)) {
26
+ cancel("Operation cancelled.");
27
+ return;
28
+ }
29
+ provider = provChoice;
30
+ }
31
+
32
+ const provInfo = AI_PROVIDERS[normalizeProviderName(provider)];
33
+ const enteredKey = await password({
34
+ message: `Enter your ${provInfo?.name || provider} API key:`,
35
+ validate(value) {
36
+ if (!value || !value.trim()) return "API key cannot be empty";
37
+ },
38
+ });
39
+
40
+ if (isCancel(enteredKey)) {
41
+ cancel("Operation cancelled.");
42
+ return;
43
+ }
44
+
45
+ apiKey = enteredKey;
46
+ }
47
+
48
+ const norm = normalizeProviderName(provider || "gemini");
49
+ const provInfo = AI_PROVIDERS[norm];
50
+
51
+ await setApiKey(norm, apiKey);
52
+
53
+ console.log(
54
+ chalk.green(`\n✔ ${provInfo?.name || norm} API key saved successfully.`)
55
+ );
10
56
  console.log(
11
57
  chalk.gray(
12
58
  `Stored securely in your OS credential manager (service: ${getCredentialServiceName()}).`
@@ -19,8 +65,13 @@ const setKeyAction = async (apiKey) => {
19
65
  };
20
66
 
21
67
  export const setkey = new Command("set-key")
22
- .description("Store your Gemini API key securely (keytar)")
23
- .argument("<API_KEY>", "Your Gemini API key")
68
+ .description("Store your AI provider API key securely in keytar (Gemini, OpenAI, Grok)")
69
+ .argument("[API_KEY]", "Your AI provider API key")
70
+ .option(
71
+ "-p, --provider <provider>",
72
+ "AI provider: gemini (default), openai, or grok",
73
+ "gemini"
74
+ )
24
75
  .alias("set")
25
76
  .alias("setkey")
26
77
  .action(setKeyAction);
@@ -12,7 +12,10 @@ import {setkey} from "./commands/config/setkey.js"
12
12
  import {launch} from "./commands/General/openApp.js"
13
13
  import {search} from "./commands/General/searchYoutube.js"
14
14
  import {play} from "./commands/General/playSong.js"
15
- import {packageJson} from "./package.json";
15
+ import { createRequire } from "module";
16
+ const require = createRequire(import.meta.url);
17
+ const packageJson = require("../../../package.json");
18
+
16
19
 
17
20
  const main = async()=>{
18
21
 
@@ -0,0 +1,159 @@
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
+ };
@@ -2,5 +2,5 @@ import "./env.js";
2
2
 
3
3
  export const config = {
4
4
  googleApiKey : process.env.GOOGLE_GENERATIVE_AI_API_KEY || "",
5
- model : process.env.ORBITAL_MODEL || "gemini-3.5-flash-lite"
5
+ model : process.env.ORBITAL_MODEL || "gemini-2.5-flash"
6
6
  }
@@ -1,13 +1,19 @@
1
1
  import { google } from "@ai-sdk/google";
2
2
  import chalk from "chalk";
3
- import { requireGeminiApiKeySync } from "../lib/orbitalConfig.js";
3
+ import {
4
+ normalizeProviderName,
5
+ requireApiKeySync,
6
+ hasApiKeySync,
7
+ } from "../lib/orbitalConfig.js";
4
8
 
5
9
  export const availableTools = [
10
+ // Google Gemini Native Tools
6
11
  {
7
12
  id: "google_search",
8
13
  name: "Google Search",
9
14
  description:
10
15
  "Access the latest information using Google Search. Useful for current events, news, and real-time information",
16
+ provider: "gemini",
11
17
  getTool: () => google.tools.googleSearch({}),
12
18
  enabled: false,
13
19
  },
@@ -16,6 +22,7 @@ export const availableTools = [
16
22
  name: "Code Execution",
17
23
  description:
18
24
  "Generate and execute Python code to perform calculations, solve problems or provide accurate information",
25
+ provider: "gemini",
19
26
  getTool: () => google.tools.codeExecution({}),
20
27
  enabled: false,
21
28
  },
@@ -24,45 +31,41 @@ export const availableTools = [
24
31
  name: "URL Context",
25
32
  description:
26
33
  "Provide specific URLs that you want the model to analyse directly from the prompt. Supports up to 20 URLs per request.",
34
+ provider: "gemini",
27
35
  getTool: () => google.tools.urlContext({}),
28
36
  enabled: false,
29
37
  },
30
38
  ];
31
39
 
32
- export const getEnabledTools = () => {
40
+ export const getToolsForProvider = (provider = "gemini") => {
41
+ const norm = normalizeProviderName(provider);
42
+ return availableTools.filter(
43
+ (tool) => tool.provider === "all" || tool.provider === norm
44
+ );
45
+ };
46
+
47
+ export const getEnabledTools = (provider = "gemini") => {
48
+ const norm = normalizeProviderName(provider);
33
49
  const tools = {};
34
50
 
35
51
  try {
36
- const enabledToolCount = availableTools.filter((t) => t.enabled).length;
37
- if (enabledToolCount > 0 && !process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
38
-
39
- requireGeminiApiKeySync();
40
- }
52
+ const providerTools = getToolsForProvider(norm);
41
53
 
42
- for (const toolConfig of availableTools) {
54
+ for (const toolConfig of providerTools) {
43
55
  if (toolConfig.enabled) {
56
+ if (toolConfig.provider === "gemini" && !hasApiKeySync("gemini")) {
57
+ requireApiKeySync("gemini");
58
+ }
44
59
  tools[toolConfig.id] = toolConfig.getTool();
45
60
  }
46
61
  }
47
62
 
48
- if (Object.keys(tools).length > 0) {
49
- console.log(
50
- chalk.gray(`[DEBUG] Enabled tools: ${Object.keys(tools).join(", ")}`)
51
- );
52
- } else {
53
- console.log(chalk.yellow(`[DEBUG] No tools enabled`));
54
- }
55
-
56
63
  return Object.keys(tools).length > 0 ? tools : undefined;
57
64
  } catch (error) {
58
65
  console.error(
59
66
  chalk.red(`[ERROR] Failed to initialize tools:`),
60
67
  error?.message || error
61
68
  );
62
- console.error(
63
- chalk.yellow(`Make sure you have @ai-sdk/google version 2.0+ installed`)
64
- );
65
- console.error(chalk.yellow(`Run: npm install @ai-sdk/google@latest`));
66
69
  return undefined;
67
70
  }
68
71
  };
@@ -72,49 +75,27 @@ export const toggleTool = (toolId) => {
72
75
 
73
76
  if (tool) {
74
77
  tool.enabled = !tool.enabled;
75
- console.log(
76
- chalk.gray(`[DEBUG] Tool ${toolId} toggled to ${tool.enabled}`)
77
- );
78
78
  return tool.enabled;
79
79
  }
80
80
 
81
- console.log(chalk.red(`[DEBUG] Tool ${toolId} not found`));
82
81
  return false;
83
82
  };
84
83
 
85
84
  export const toogleTool = toggleTool;
86
85
 
87
86
  export const enableTools = (toolIds = []) => {
88
- console.log(chalk.gray(`[DEBUG] enableTools called with:`), toolIds);
89
-
90
87
  availableTools.forEach((tool) => {
91
- const wasEnabled = tool.enabled;
92
88
  tool.enabled = toolIds.includes(tool.id);
93
-
94
- if (tool.enabled !== wasEnabled) {
95
- console.log(
96
- chalk.gray(`[DEBUG] ${tool.id}: ${wasEnabled} -> ${tool.enabled}`)
97
- );
98
- }
99
89
  });
100
-
101
- const enabledCount = availableTools.filter((t) => t.enabled).length;
102
- console.log(
103
- chalk.gray(
104
- `[DEBUG] Total tools enabled: ${enabledCount} / ${availableTools.length}`
105
- )
106
- );
107
90
  };
108
91
 
109
92
  export const getEnabledToolNames = () => {
110
- const names = availableTools.filter((t) => t.enabled).map((t) => t.name);
111
- console.log(chalk.gray(`[DEBUG] getEnabledToolNames returning:`), names);
112
- return names;
93
+ return availableTools.filter((t) => t.enabled).map((t) => t.name);
113
94
  };
114
95
 
115
96
  export const resetTools = () => {
116
97
  availableTools.forEach((tool) => {
117
98
  tool.enabled = false;
118
99
  });
119
- console.log(chalk.gray(`[DEBUG] All tools have been reset (disabled)`));
120
100
  };
101
+