[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/thaolaptrinh/commandcode-api-proxy/main/src/proxy.ts [Back]  [Original]

#!/usr/bin/env node

import { loadConfig, fetchLatestCliVersion } from "@/config.js";
import { createServer } from "@/server.js";
import { saveApiKey, promptForApiKey, readAuthKey, deleteAuth } from "@/auth.js";
import { setupOpenCodeConfig } from "@/setup/opencode.js";
import { setupClaudeCodeConfig } from "@/setup/claude-code.js";
import { logger, initLogger } from "@/logger.js";
import { getProxyVersion } from "@/version.js";

const args = process.argv.slice(2);

if (args[0] === "auth") {
  const sub = args[1];
  if (sub === "login") {
    const force = args.includes("--force");
    const existing = readAuthKey();
    if (existing && !force) {
      console.log("\n  You are already logged in. Use `auth login --force` to overwrite.\n");
      process.exit(0);
    }
    console.log("\n  Get your API key from https://commandcode.ai/settings\n");
    const key = await promptForApiKey();
    if (!key) {
      console.error("  FATAL: API key is required.\n");
      process.exit(1);
    }
    saveApiKey(key);
    console.log("   API key saved to ~/.config/commandcode-api-proxy/auth.json\n");
  } else if (sub === "logout") {
    deleteAuth();
    console.log("\n   API key removed\n");
  } else {
    console.error("\n  Usage: commandcode-api-proxy auth \n");
  }
  process.exit(0);
}

if (args.includes("--setup-opencode")) {
  await setupOpenCodeConfig();
  process.exit(0);
}

if (args.includes("--setup-claude-code")) {
  const force = args.includes("--force");
  await setupClaudeCodeConfig(force);
  process.exit(0);
}

const config = loadConfig();
initLogger(config.logLevel);

logger.info(
  `API key source: ${process.env.CC_API_KEY ? "env CC_API_KEY" : config.apiKey ? "auth.json" : "none"} (length: ${config.apiKey?.length ?? 0})`,
);

if (!process.env.CC_CLI_VERSION) {
  const latest = await fetchLatestCliVersion();
  if (latest) config.ccVersion = latest;
}

if (!config.apiKey) {
  console.log("\n  No Command Code API key found.");
  console.log("  You can get one from https://commandcode.ai/settings\n");
  const key = await promptForApiKey();
  if (!key) {
    console.error("  FATAL: API key is required.\n");
    process.exit(1);
  }
  saveApiKey(key);
  config.apiKey = key;
  console.log("   API key saved to ~/.config/commandcode-api-proxy/auth.json\n");
}

const server = createServer(config);

server.listen(config.port, config.host, () => {
  console.log(`\n  Command Code API Proxy v${getProxyVersion()}`);
  console.log(`  ${"=".repeat(50)}`);
  console.log(`  Listening on  http://${config.host}:${config.port}`);
  console.log(`  Auth: ${config.apiKey ? "ENABLED (Bearer token or x-api-key)" : "DISABLED"}`);
  console.log("");
  console.log("  Endpoints:");
  console.log("    GET  /health");
  console.log("    GET  /v1/models");
  console.log("    POST /v1/chat/completions  (OpenAI format)");
  console.log("    POST /v1/messages          (Anthropic format)");
  console.log("    POST /v1/messages/count_tokens  (Anthropic format)");
  console.log("");
  console.log("  Press Ctrl+C to stop\n");
});

// Never let an unexpected async error crash the proxy silently  log and keep
// serving. (Route handlers already catch their own errors; this is a backstop.)
process.on("unhandledRejection", (reason) => {
  logger.error("[fatal] Unhandled promise rejection:", reason);
});
process.on("uncaughtException", (err) => {
  logger.error("[fatal] Uncaught exception:", err);
});

const shutdown = (signal: string): void => {
  logger.info(`Received ${signal}, shutting down...`);
  // Don't hang forever waiting on a stuck streaming connection.
  const force = setTimeout(() => {
    logger.warn("Forcing shutdown after 10s timeout");
    process.exit(1);
  }, 10_000);
  force.unref();
  server.close(() => process.exit(0));
};

process.on("SIGINT", () => shutdown("SIGINT"));
process.on("SIGTERM", () => shutdown("SIGTERM"));

Web Proxy Viewer  |  New URL  |  Original Page