aex-code 1.0.0
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/LICENSE +21 -0
- package/README.md +71 -0
- package/bin/aex-code.js +3 -0
- package/package.json +42 -0
- package/src/commands/chat.js +91 -0
- package/src/commands/config.js +21 -0
- package/src/commands/edit.js +33 -0
- package/src/commands/init.js +16 -0
- package/src/commands/run.js +21 -0
- package/src/index.js +72 -0
- package/src/utils/configManager.js +25 -0
- package/src/utils/openrouter.js +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Md. Abid Hasan Rafi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the “Software”), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+

|
|
2
|
+
# A E X - C O D E
|
|
3
|
+
|
|
4
|
+
AEX Code is a CLI application that functions as an advanced coding agent similar to Claude Code. It is optimized for free-tier usage via OpenRouter models and provides a clean UI with both interactive and command-based workflows. This project was developed by [Md. Abid Hasan Rafi](https://abidhasanrafi.github.io) and powered by [AI Extension](https://aiextension.org).
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- **Code Generation and Explanation:** Interactive terminal sessions within your workspace.
|
|
9
|
+
- **File Management:** Create, review, and edit files with context-aware AI assistance.
|
|
10
|
+
- **Safe Shell Commands:** Execute shell commands with controlled output handling.
|
|
11
|
+
- **Multi-file Context Processing:** Build rich context from your codebase across multiple files.
|
|
12
|
+
- **Configurable Models:** Use OpenRouter API keys to select preferred models. Defaults to `qwen/qwen3-coder:free`.
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Node.js >= 16
|
|
17
|
+
|
|
18
|
+
## Setup and Configuration
|
|
19
|
+
|
|
20
|
+
1. Install globally:
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g aex-code
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. Configure your OpenRouter API key:
|
|
26
|
+
```bash
|
|
27
|
+
aex-code config --set-key "sk-or-v1-..."
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Set your preferred model (optional):
|
|
31
|
+
```bash
|
|
32
|
+
aex-code config --set-model "anthropic/claude-3.5-sonnet:beta"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
### Interactive Shell
|
|
38
|
+
|
|
39
|
+
Start an interactive session with the AI agent. The session maintains context and streams responses.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
aex-code chat
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Analyze and Edit a File
|
|
46
|
+
|
|
47
|
+
Analyze and modify local files using AI.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
aex-code edit src/index.js
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Initialize Configuration
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
aex-code init
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Run Commands
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
aex-code run "npm install"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Development and Contribution
|
|
66
|
+
|
|
67
|
+
The project follows a modular architecture where commands are organized under `src/commands`, making it easy to extend and maintain.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
package/bin/aex-code.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aex-code",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Next-Gen OpenRouter Coding Agent CLI - Powered by AI Extension",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node ./bin/aex-code.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"aex-code",
|
|
11
|
+
"ai",
|
|
12
|
+
"cli",
|
|
13
|
+
"coding-agent",
|
|
14
|
+
"agent",
|
|
15
|
+
"openrouter",
|
|
16
|
+
"llm"
|
|
17
|
+
],
|
|
18
|
+
"author": "Your Name / AI Extension",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"boxen": "^7.1.1",
|
|
23
|
+
"chalk": "^4.1.2",
|
|
24
|
+
"commander": "^14.0.3",
|
|
25
|
+
"dotenv": "^17.3.1",
|
|
26
|
+
"figlet": "^1.11.0",
|
|
27
|
+
"fs-extra": "^11.3.4",
|
|
28
|
+
"inquirer": "^8.2.7",
|
|
29
|
+
"openai": "^6.32.0",
|
|
30
|
+
"ora": "^5.4.1"
|
|
31
|
+
},
|
|
32
|
+
"bin": {
|
|
33
|
+
"aex-code": "bin/aex-code.js"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16.0.0"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/yourusername/aex-code.git"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import boxen from 'boxen';
|
|
7
|
+
import { createClient, getDefaultModel, debugApiKey } from '../utils/openrouter.js';
|
|
8
|
+
|
|
9
|
+
const SYSTEM_PROMPT = `You are AEX Code, an advanced coding agent. You provide short, actionable coding assistance.
|
|
10
|
+
If asked to edit a file, provide the full file contents or explicitly outline what to change in the file.
|
|
11
|
+
Try to use <tool_call> JSON format for file writes if possible.
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
let conversationHistory = [];
|
|
15
|
+
|
|
16
|
+
export async function chatCommand() {
|
|
17
|
+
console.clear();
|
|
18
|
+
const chatHeader = boxen(chalk.greenBright.bold('A E X C O D E'), {
|
|
19
|
+
padding: 1, margin: 1, borderStyle: 'bold', borderColor: 'green'
|
|
20
|
+
});
|
|
21
|
+
console.log(chatHeader);
|
|
22
|
+
console.log(chalk.green(` ■ Target Model : ${chalk.gray(getDefaultModel())}`));
|
|
23
|
+
console.log(chalk.green(` ■ Exit Command : ${chalk.gray("'exit'")}\n`));
|
|
24
|
+
|
|
25
|
+
let client;
|
|
26
|
+
try {
|
|
27
|
+
client = createClient();
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.log(chalk.red(e.message));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
conversationHistory.push({ role: 'system', content: SYSTEM_PROMPT });
|
|
34
|
+
|
|
35
|
+
while (true) {
|
|
36
|
+
process.stdout.write('\n');
|
|
37
|
+
const { userInput } = await inquirer.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'input',
|
|
40
|
+
name: 'userInput',
|
|
41
|
+
message: chalk.bold.green('■ USER : '),
|
|
42
|
+
}
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
if (userInput.trim().toLowerCase() === 'exit') {
|
|
46
|
+
console.log(chalk.green('\n■ SESSION TERMINATED.'));
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
conversationHistory.push({ role: 'user', content: userInput });
|
|
51
|
+
|
|
52
|
+
process.stdout.write('\n');
|
|
53
|
+
const spinner = ora({
|
|
54
|
+
text: chalk.italic.green('AEX PROCESSSING...'),
|
|
55
|
+
color: 'green',
|
|
56
|
+
}).start();
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const response = await client.chat.completions.create({
|
|
60
|
+
model: getDefaultModel(),
|
|
61
|
+
messages: conversationHistory,
|
|
62
|
+
stream: true,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
spinner.stop();
|
|
66
|
+
process.stdout.write(`${chalk.bgGreen.black.bold(' ■ AEX AGENT ')} `);
|
|
67
|
+
|
|
68
|
+
let fullContent = '';
|
|
69
|
+
for await (const chunk of response) {
|
|
70
|
+
const text = chunk.choices[0]?.delta?.content || '';
|
|
71
|
+
process.stdout.write(chalk.green(text));
|
|
72
|
+
fullContent += text;
|
|
73
|
+
}
|
|
74
|
+
console.log('\n');
|
|
75
|
+
conversationHistory.push({ role: 'assistant', content: fullContent });
|
|
76
|
+
|
|
77
|
+
} catch (e) {
|
|
78
|
+
spinner.stop();
|
|
79
|
+
console.log(chalk.bgRed.white.bold(' ■ ERROR '));
|
|
80
|
+
if (e.status === 429) {
|
|
81
|
+
console.log(chalk.red(`\nRate Limit or Provider Error (429): The active model (${getDefaultModel()}) is currently overloaded.`));
|
|
82
|
+
console.log(chalk.yellow(`Tip: Try swapping your model using:\naex-code config --set-model "google/gemini-2.0-flash-lite-preview-02-05:free"\n`));
|
|
83
|
+
} else if (e.status === 401) {
|
|
84
|
+
console.log(chalk.red(`\nUnauthorized (401): Your API key is invalid or not recognized by OpenRouter.`));
|
|
85
|
+
} else {
|
|
86
|
+
console.log(chalk.red(`\nFailed to reach OpenRouter API: ${e.message}\n`));
|
|
87
|
+
}
|
|
88
|
+
conversationHistory.pop();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { saveConfig } from '../utils/configManager.js';
|
|
3
|
+
|
|
4
|
+
export const configCommand = (options) => {
|
|
5
|
+
if (options.setKey) {
|
|
6
|
+
// Automatically strip accidental quotes just in case
|
|
7
|
+
const cleanKey = options.setKey.replace(/^['"]|['"]$/g, '');
|
|
8
|
+
saveConfig({ OPENROUTER_API_KEY: cleanKey });
|
|
9
|
+
console.log(chalk.green('✔ OpenRouter API Key configured successfully.'));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (options.setModel) {
|
|
13
|
+
const cleanModel = options.setModel.replace(/^['"]|['"]$/g, '');
|
|
14
|
+
saveConfig({ OPENROUTER_DEFAULT_MODEL: cleanModel });
|
|
15
|
+
console.log(chalk.green(`✔ Default Model set to ${cleanModel}.`));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!options.setKey && !options.setModel) {
|
|
19
|
+
console.log(chalk.yellow('Usage: aex-code config --set-key <key> or --set-model <model>'));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { createClient, getDefaultModel } from '../utils/openrouter.js';
|
|
6
|
+
|
|
7
|
+
export async function editCommand(file, options) {
|
|
8
|
+
if (!fs.existsSync(file)) {
|
|
9
|
+
console.log(chalk.red(`File ${file} does not exist.`));
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
console.log(chalk.blue(`Suggesting edits for ${file}...`));
|
|
14
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
15
|
+
|
|
16
|
+
const client = createClient();
|
|
17
|
+
const spinner = ora('AEX Code is analyzing the file...').start();
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const response = await client.chat.completions.create({
|
|
21
|
+
model: getDefaultModel(),
|
|
22
|
+
messages: [
|
|
23
|
+
{ role: 'system', content: 'You are AEX Code. Please evaluate this code file and suggest improvements. Keep output concise.' },
|
|
24
|
+
{ role: 'user', content: `File name: ${file}\n\nCode:\n\`\`\`\n${content}\n\`\`\`` }
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
spinner.stop();
|
|
28
|
+
console.log(chalk.green(response.choices[0].message.content));
|
|
29
|
+
} catch (e) {
|
|
30
|
+
spinner.fail('Error analyzing file.');
|
|
31
|
+
console.log(chalk.red(e.message));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export function initCommand() {
|
|
6
|
+
console.log(chalk.green('Initializing AEX Code project...'));
|
|
7
|
+
const configPath = path.join(process.cwd(), '.aex-code');
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(configPath)) {
|
|
10
|
+
fs.mkdirSync(configPath);
|
|
11
|
+
fs.writeFileSync(path.join(configPath, 'rules.md'), '# AEX Code Project Rules\n\nAdd project-specific rules here.');
|
|
12
|
+
console.log(chalk.green('✔ Initialized .aex-code directory securely.'));
|
|
13
|
+
} else {
|
|
14
|
+
console.log(chalk.yellow('AEX Code project already initialized in this directory.'));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
|
|
5
|
+
export function runCommand(cmdString) {
|
|
6
|
+
const spinner = ora(`Running: ${cmdString}`).start();
|
|
7
|
+
|
|
8
|
+
exec(cmdString, (error, stdout, stderr) => {
|
|
9
|
+
spinner.stop();
|
|
10
|
+
if (error) {
|
|
11
|
+
console.error(chalk.red(`Error executing command: ${error.message}`));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (stderr) {
|
|
15
|
+
console.error(chalk.yellow(`Output:\n${stderr}`));
|
|
16
|
+
}
|
|
17
|
+
if (stdout) {
|
|
18
|
+
console.log(chalk.green(`Result:\n${stdout}`));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import figlet from 'figlet';
|
|
4
|
+
import boxen from 'boxen';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
import { initCommand } from './commands/init.js';
|
|
10
|
+
import { chatCommand } from './commands/chat.js';
|
|
11
|
+
import { configCommand } from './commands/config.js';
|
|
12
|
+
import { editCommand } from './commands/edit.js';
|
|
13
|
+
import { runCommand } from './commands/run.js';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
const packageJson = JSON.parse(
|
|
19
|
+
fs.readFileSync(path.join(__dirname, '../package.json'), 'utf-8')
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const program = new Command();
|
|
23
|
+
|
|
24
|
+
const bannerText = figlet.textSync('AEX Code', { font: 'ANSI Shadow', horizontalLayout: 'fitted' });
|
|
25
|
+
const coloredBanner = chalk.greenBright(bannerText);
|
|
26
|
+
const rawTagline = ' NEXT-GEN OPENROUTER CODING AGENT - POWERED BY AI EXTENSION ';
|
|
27
|
+
|
|
28
|
+
const finalBanner = boxen(`${coloredBanner}\n\n${chalk.bgGreen.black.bold(rawTagline)}`, {
|
|
29
|
+
padding: 1,
|
|
30
|
+
margin: 1,
|
|
31
|
+
borderStyle: 'bold',
|
|
32
|
+
borderColor: 'green',
|
|
33
|
+
textAlignment: 'center',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.name('aex-code')
|
|
38
|
+
.description(finalBanner)
|
|
39
|
+
.version(packageJson.version);
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.command('init')
|
|
43
|
+
.description('Initialize AEX Code in the current workspace')
|
|
44
|
+
.action(initCommand);
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.command('chat')
|
|
48
|
+
.description('Start an interactive chat session with AEX Code')
|
|
49
|
+
.action(chatCommand);
|
|
50
|
+
|
|
51
|
+
program
|
|
52
|
+
.command('config')
|
|
53
|
+
.description('Configure API keys and default models for AEX Code')
|
|
54
|
+
.option('--set-key <key>', 'Set OpenRouter API key')
|
|
55
|
+
.option('--set-model <model>', 'Set default OpenRouter model')
|
|
56
|
+
.action(configCommand);
|
|
57
|
+
|
|
58
|
+
program
|
|
59
|
+
.command('edit <file>')
|
|
60
|
+
.description('Edit or analyze a file locally')
|
|
61
|
+
.action(editCommand);
|
|
62
|
+
|
|
63
|
+
program
|
|
64
|
+
.command('run <cmd>')
|
|
65
|
+
.description('Run a shell command safely')
|
|
66
|
+
.action(runCommand);
|
|
67
|
+
|
|
68
|
+
program.parse(process.argv);
|
|
69
|
+
|
|
70
|
+
if (!process.argv.slice(2).length) {
|
|
71
|
+
program.outputHelp();
|
|
72
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
const CONFIG_PATH = path.join(os.homedir(), '.aex-code.json');
|
|
6
|
+
|
|
7
|
+
export const loadConfig = () => {
|
|
8
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
9
|
+
try {
|
|
10
|
+
const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
11
|
+
return JSON.parse(raw);
|
|
12
|
+
} catch (e) {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return {};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const saveConfig = (newConfig) => {
|
|
20
|
+
const config = { ...loadConfig(), ...newConfig };
|
|
21
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
|
|
22
|
+
return config;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const getConfig = (key) => loadConfig()[key];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { getConfig } from './configManager.js';
|
|
3
|
+
import dotenv from 'dotenv';
|
|
4
|
+
dotenv.config({ path: '.env.local', silent: true });
|
|
5
|
+
|
|
6
|
+
export const createClient = () => {
|
|
7
|
+
const apiKey = process.env.OPENROUTER_API_KEY || getConfig('OPENROUTER_API_KEY');
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
throw new Error('OpenRouter API Key not found. Please run "aex-code config" to setup your key.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return new OpenAI({
|
|
13
|
+
baseURL: 'https://openrouter.ai/api/v1',
|
|
14
|
+
apiKey: apiKey.trim(),
|
|
15
|
+
defaultHeaders: {
|
|
16
|
+
'HTTP-Referer': 'https://github.com/aex-code',
|
|
17
|
+
'X-Title': 'AEX Code CLI',
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const debugApiKey = () => {
|
|
23
|
+
const key = process.env.OPENROUTER_API_KEY || getConfig('OPENROUTER_API_KEY');
|
|
24
|
+
if (!key) return null;
|
|
25
|
+
return `${key.substring(0, 10)}...${key.slice(-4)}`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const getDefaultModel = () => {
|
|
29
|
+
return process.env.OPENROUTER_DEFAULT_MODEL || getConfig('OPENROUTER_DEFAULT_MODEL') || 'qwen/qwen-2.5-coder-32b-instruct:free';
|
|
30
|
+
};
|