Merge branch 'main' into bugfix/fix-code-args-handling
This commit is contained in:
@@ -41,6 +41,7 @@ The `config.json` file has several key sections:
|
||||
- **`LOG`** (optional): You can enable logging by setting it to `true`. The log file will be located at `$HOME/.claude-code-router.log`.
|
||||
- **`APIKEY`** (optional): You can set a secret key to authenticate requests. When set, clients must provide this key in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. Example: `"APIKEY": "your-secret-key"`.
|
||||
- **`HOST`** (optional): You can set the host address for the server. If `APIKEY` is not set, the host will be forced to `127.0.0.1` for security reasons to prevent unauthorized access. Example: `"HOST": "0.0.0.0"`.
|
||||
- **`NON_INTERACTIVE_MODE`** (optional): When set to `true`, enables compatibility with non-interactive environments like GitHub Actions, Docker containers, or other CI/CD systems. This sets appropriate environment variables (`CI=true`, `FORCE_COLOR=0`, etc.) and configures stdin handling to prevent the process from hanging in automated environments. Example: `"NON_INTERACTIVE_MODE": true`.
|
||||
|
||||
- **`Providers`**: Used to configure different model providers.
|
||||
- **`Router`**: Used to set up routing rules. `default` specifies the default model, which will be used for all requests if no other route is configured.
|
||||
@@ -54,6 +55,7 @@ Here is a comprehensive example:
|
||||
"PROXY_URL": "http://127.0.0.1:7890",
|
||||
"LOG": true,
|
||||
"API_TIMEOUT_MS": 600000,
|
||||
"NON_INTERACTIVE_MODE": false,
|
||||
"Providers": [
|
||||
{
|
||||
"name": "openrouter",
|
||||
@@ -407,6 +409,7 @@ jobs:
|
||||
cat << 'EOF' > $HOME/.claude-code-router/config.json
|
||||
{
|
||||
"log": true,
|
||||
"NON_INTERACTIVE_MODE": true,
|
||||
"OPENAI_API_KEY": "${{ secrets.OPENAI_API_KEY }}",
|
||||
"OPENAI_BASE_URL": "https://api.deepseek.com",
|
||||
"OPENAI_MODEL": "deepseek-chat"
|
||||
@@ -428,6 +431,8 @@ jobs:
|
||||
anthropic_api_key: "any-string-is-ok"
|
||||
```
|
||||
|
||||
> **Note**: When running in GitHub Actions or other automation environments, make sure to set `"NON_INTERACTIVE_MODE": true` in your configuration to prevent the process from hanging due to stdin handling issues.
|
||||
|
||||
This setup allows for interesting automations, like running tasks during off-peak hours to reduce API costs.
|
||||
|
||||
## 📝 Further Reading
|
||||
|
||||
@@ -38,6 +38,7 @@ npm install -g @musistudio/claude-code-router
|
||||
- **`LOG`** (可选): 您可以通过将其设置为 `true` 来启用日志记录。日志文件将位于 `$HOME/.claude-code-router.log`。
|
||||
- **`APIKEY`** (可选): 您可以设置一个密钥来进行身份验证。设置后,客户端请求必须在 `Authorization` 请求头 (例如, `Bearer your-secret-key`) 或 `x-api-key` 请求头中提供此密钥。例如:`"APIKEY": "your-secret-key"`。
|
||||
- **`HOST`** (可选): 您可以设置服务的主机地址。如果未设置 `APIKEY`,出于安全考虑,主机地址将强制设置为 `127.0.0.1`,以防止未经授权的访问。例如:`"HOST": "0.0.0.0"`。
|
||||
- **`NON_INTERACTIVE_MODE`** (可选): 当设置为 `true` 时,启用与非交互式环境(如 GitHub Actions、Docker 容器或其他 CI/CD 系统)的兼容性。这会设置适当的环境变量(`CI=true`、`FORCE_COLOR=0` 等)并配置 stdin 处理,以防止进程在自动化环境中挂起。例如:`"NON_INTERACTIVE_MODE": true`。
|
||||
- **`Providers`**: 用于配置不同的模型提供商。
|
||||
- **`Router`**: 用于设置路由规则。`default` 指定默认模型,如果未配置其他路由,则该模型将用于所有请求。
|
||||
- **`API_TIMEOUT_MS`**: API 请求超时时间,单位为毫秒。
|
||||
@@ -50,6 +51,7 @@ npm install -g @musistudio/claude-code-router
|
||||
"PROXY_URL": "http://127.0.0.1:7890",
|
||||
"LOG": true,
|
||||
"API_TIMEOUT_MS": 600000,
|
||||
"NON_INTERACTIVE_MODE": false,
|
||||
"Providers": [
|
||||
{
|
||||
"name": "openrouter",
|
||||
@@ -402,6 +404,7 @@ jobs:
|
||||
cat << 'EOF' > $HOME/.claude-code-router/config.json
|
||||
{
|
||||
"log": true,
|
||||
"NON_INTERACTIVE_MODE": true,
|
||||
"OPENAI_API_KEY": "${{ secrets.OPENAI_API_KEY }}",
|
||||
"OPENAI_BASE_URL": "https://api.deepseek.com",
|
||||
"OPENAI_MODEL": "deepseek-chat"
|
||||
|
||||
@@ -114,5 +114,6 @@
|
||||
},
|
||||
"APIKEY": "your-secret-key",
|
||||
"HOST": "0.0.0.0",
|
||||
"API_TIMEOUT_MS": 600000
|
||||
"API_TIMEOUT_MS": 600000,
|
||||
"NON_INTERACTIVE_MODE": false
|
||||
}
|
||||
|
||||
2052
package-lock.json
generated
2052
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -47,7 +47,7 @@ export const createServer = (config: any): Server => {
|
||||
// Restart the service after a short delay to allow response to be sent
|
||||
setTimeout(() => {
|
||||
const { spawn } = require("child_process");
|
||||
spawn("ccr", ["restart"], { detached: true, stdio: "ignore" });
|
||||
spawn(process.execPath, [process.argv[1], "restart"], { detached: true, stdio: "ignore" });
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,21 +1,29 @@
|
||||
import { spawn } from "child_process";
|
||||
import {
|
||||
incrementReferenceCount,
|
||||
decrementReferenceCount,
|
||||
} from "./processCheck";
|
||||
import { closeService } from "./close";
|
||||
import { spawn, type StdioOptions } from "child_process";
|
||||
import { readConfigFile } from ".";
|
||||
import { closeService } from "./close";
|
||||
import {
|
||||
decrementReferenceCount,
|
||||
incrementReferenceCount,
|
||||
} from "./processCheck";
|
||||
|
||||
export async function executeCodeCommand(args: string[] = []) {
|
||||
// Set environment variables
|
||||
const config = await readConfigFile();
|
||||
const env = {
|
||||
const env: Record<string, string> = {
|
||||
...process.env,
|
||||
ANTHROPIC_AUTH_TOKEN: "test",
|
||||
ANTHROPIC_BASE_URL: `http://127.0.0.1:${config.PORT || 3456}`,
|
||||
API_TIMEOUT_MS: String(config.API_TIMEOUT_MS ?? 600000), // Default to 10 minutes if not set
|
||||
};
|
||||
|
||||
// Non-interactive mode for automation environments
|
||||
if (config.NON_INTERACTIVE_MODE) {
|
||||
env.CI = "true";
|
||||
env.FORCE_COLOR = "0";
|
||||
env.NODE_NO_READLINE = "1";
|
||||
env.TERM = "dumb";
|
||||
}
|
||||
|
||||
// Set ANTHROPIC_SMALL_FAST_MODEL if it exists in config
|
||||
if (config?.ANTHROPIC_SMALL_FAST_MODEL) {
|
||||
env.ANTHROPIC_SMALL_FAST_MODEL = config.ANTHROPIC_SMALL_FAST_MODEL;
|
||||
@@ -35,13 +43,23 @@ export async function executeCodeCommand(args: string[] = []) {
|
||||
// Properly join arguments to preserve spaces in quotes
|
||||
// Wrap each argument in double quotes to preserve single and double quotes inside arguments
|
||||
const joinedArgs = args.length > 0 ? args.map(arg => `"${arg.replace(/\"/g, '\\"')}"`).join(" ") : "";
|
||||
|
||||
|
||||
// 🔥 CONFIG-DRIVEN: stdio configuration based on environment
|
||||
const stdioConfig: StdioOptions = config.NON_INTERACTIVE_MODE
|
||||
? ["pipe", "inherit", "inherit"] // Pipe stdin for non-interactive
|
||||
: "inherit"; // Default inherited behavior
|
||||
|
||||
const claudeProcess = spawn(claudePath + (joinedArgs ? ` ${joinedArgs}` : ""), [], {
|
||||
env,
|
||||
stdio: "inherit",
|
||||
stdio: stdioConfig,
|
||||
shell: true,
|
||||
});
|
||||
|
||||
// Close stdin for non-interactive mode
|
||||
if (config.NON_INTERACTIVE_MODE) {
|
||||
claudeProcess.stdin?.end();
|
||||
}
|
||||
|
||||
claudeProcess.on("error", (error) => {
|
||||
console.error("Failed to start claude command:", error.message);
|
||||
console.log(
|
||||
|
||||
@@ -174,5 +174,6 @@
|
||||
"think": "gemini-cli,gemini-2.5-pro",
|
||||
"longContext": "gemini-cli,gemini-2.5-pro",
|
||||
"webSearch": "gemini-cli,gemini-2.5-flash"
|
||||
}
|
||||
}
|
||||
},
|
||||
"NON_INTERACTIVE_MODE": false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user