Spaces:
Running
Running
File size: 6,537 Bytes
780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 98c9143 780d695 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | #!/usr/bin/env node
import { defineCommand } from "citty"
import clipboard from "clipboardy"
import consola from "consola"
import { serve, type ServerHandler } from "srvx"
import invariant from "tiny-invariant"
import { mergeConfigWithDefaults } from "./lib/config"
import { initOpencodeVersion } from "./lib/opencode"
import { ensurePaths } from "./lib/paths"
import { initProxyFromEnv } from "./lib/proxy"
import { generateEnvScript } from "./lib/shell"
import { state } from "./lib/state"
import { logUser, setupCopilotToken, setupGitHubToken } from "./lib/token"
import {
cacheMacMachineId,
cacheModels,
cacheVSCodeVersion,
cacheVsCodeSessionId,
cacheVsCodeDeviceId,
} from "./lib/utils"
interface RunServerOptions {
port: number
verbose: boolean
accountType: string
manual: boolean
rateLimit?: number
rateLimitWait: boolean
githubToken?: string
claudeCode: boolean
showToken: boolean
proxyEnv: boolean
}
export async function runServer(options: RunServerOptions): Promise<void> {
// Work around unjs/consola#357 until a release includes PR #359.
consola.options.throttle = 0
// Ensure config is merged with defaults at startup
mergeConfigWithDefaults()
await initOpencodeVersion()
if (options.proxyEnv) {
initProxyFromEnv()
}
state.verbose = options.verbose
if (options.verbose) {
consola.level = 5
consola.info("Verbose logging enabled")
}
state.accountType = options.accountType
if (options.accountType !== "individual") {
consola.info(`Using ${options.accountType} plan GitHub account`)
}
state.manualApprove = options.manual
state.rateLimitSeconds = options.rateLimit
state.rateLimitWait = options.rateLimitWait
state.showToken = options.showToken
await ensurePaths()
await cacheVSCodeVersion()
cacheMacMachineId()
cacheVsCodeSessionId()
await cacheVsCodeDeviceId()
if (options.githubToken) {
state.githubToken = options.githubToken
consola.info("Using provided GitHub token")
await logUser()
} else {
await setupGitHubToken()
}
await setupCopilotToken()
await cacheModels()
consola.info(
`Available models: \n${state.models?.data.map((model) => `- ${model.id}`).join("\n")}`,
)
const serverUrl = `http://localhost:${options.port}`
if (options.claudeCode) {
consola.log(
"\n💡 Tip: The --claude-code flag simply generates a clipboard command for launching Claude Code. \n"
+ "All models remain fully accessible without this flag, just configure the model ID directly in your settings.json file.",
)
invariant(state.models, "Models should be loaded by now")
const selectedModel = await consola.prompt(
"Select a model to use with Claude Code",
{
type: "select",
options: state.models.data.map((model) => model.id),
},
)
const selectedSmallModel = await consola.prompt(
"Select a small model to use with Claude Code",
{
type: "select",
options: state.models.data.map((model) => model.id),
},
)
const command = generateEnvScript(
{
ANTHROPIC_BASE_URL: serverUrl,
ANTHROPIC_AUTH_TOKEN: "dummy",
ANTHROPIC_MODEL: selectedModel,
ANTHROPIC_DEFAULT_SONNET_MODEL: selectedModel,
ANTHROPIC_DEFAULT_HAIKU_MODEL: selectedSmallModel,
DISABLE_NON_ESSENTIAL_MODEL_CALLS: "1",
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
CLAUDE_CODE_ATTRIBUTION_HEADER: "0",
CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION: "false",
CLAUDE_CODE_DISABLE_TERMINAL_TITLE: "true",
CLAUDE_CODE_ENABLE_AWAY_SUMMARY: "0",
CLAUDE_PLUGIN_ENABLE_QUESTION_RULES: "true",
},
"claude",
)
try {
clipboard.writeSync(command)
consola.success("Copied Claude Code command to clipboard!")
} catch {
consola.warn(
"Failed to copy to clipboard. Here is the Claude Code command:",
)
consola.log(command)
}
}
consola.box(
`🌐 Usage Viewer: ${serverUrl}/usage-viewer?endpoint=${serverUrl}/usage`,
)
const { server } = await import("./server")
serve({
fetch: server.fetch as ServerHandler,
port: options.port,
bun: {
idleTimeout: 0,
},
})
}
export const start = defineCommand({
meta: {
name: "start",
description: "Start the Copilot API server",
},
args: {
port: {
alias: "p",
type: "string",
default: "4141",
description: "Port to listen on",
},
verbose: {
alias: "v",
type: "boolean",
default: false,
description: "Enable verbose logging",
},
"account-type": {
alias: "a",
type: "string",
default: "individual",
description: "Account type to use (individual, business, enterprise)",
},
manual: {
type: "boolean",
default: false,
description: "Enable manual request approval",
},
"rate-limit": {
alias: "r",
type: "string",
description: "Rate limit in seconds between requests",
},
wait: {
alias: "w",
type: "boolean",
default: false,
description:
"Wait instead of error when rate limit is hit. Has no effect if rate limit is not set",
},
"github-token": {
alias: "g",
type: "string",
description:
"Provide GitHub token directly (must be generated using the `auth` subcommand)",
},
"claude-code": {
alias: "c",
type: "boolean",
default: false,
description:
"Generate a command to launch Claude Code with Copilot API config",
},
"show-token": {
type: "boolean",
default: false,
description: "Show GitHub and Copilot tokens on fetch and refresh",
},
"proxy-env": {
type: "boolean",
default: false,
description: "Initialize proxy from environment variables",
},
},
run({ args }) {
const rateLimitRaw = args["rate-limit"]
const rateLimit =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
rateLimitRaw === undefined ? undefined : Number.parseInt(rateLimitRaw, 10)
return runServer({
port: Number.parseInt(args.port, 10),
verbose: args.verbose,
accountType: args["account-type"],
manual: args.manual,
rateLimit,
rateLimitWait: args.wait,
githubToken: args["github-token"],
claudeCode: args["claude-code"],
showToken: args["show-token"],
proxyEnv: args["proxy-env"],
})
},
})
|