@openhoo/hoopilot 0.5.6 → 0.5.8
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/README.md +17 -12
- package/dist/cli.js +28 -505
- package/dist/cli.js.map +1 -1
- package/dist/codexx.js +64 -3
- package/dist/codexx.js.map +1 -1
- package/dist/index.cjs +28 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +28 -83
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/codexx.js
CHANGED
|
@@ -6,6 +6,8 @@ import { constants as osConstants } from "os";
|
|
|
6
6
|
var DEFAULT_BASE_URL = "http://127.0.0.1:4141/v1";
|
|
7
7
|
var DEFAULT_API_KEY = "local-key";
|
|
8
8
|
var DEFAULT_CODEX_BIN = "codex";
|
|
9
|
+
var DEFAULT_MODEL = "gpt-5.5";
|
|
10
|
+
var DEFAULT_REASONING_EFFORT = "xhigh";
|
|
9
11
|
var PROXY_ENV_KEYS = [
|
|
10
12
|
"ALL_PROXY",
|
|
11
13
|
"HTTPS_PROXY",
|
|
@@ -20,6 +22,8 @@ function buildCodexxInvocation(argv, env = process.env) {
|
|
|
20
22
|
const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;
|
|
21
23
|
const apiKey = env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;
|
|
22
24
|
const command = env.CODEXX_CODEX_BIN ?? DEFAULT_CODEX_BIN;
|
|
25
|
+
const model = env.CODEXX_MODEL ?? DEFAULT_MODEL;
|
|
26
|
+
const reasoningEffort = env.CODEXX_MODEL_REASONING_EFFORT ?? DEFAULT_REASONING_EFFORT;
|
|
23
27
|
const providerConfig = [
|
|
24
28
|
'{ name = "Hoopilot"',
|
|
25
29
|
`base_url = ${JSON.stringify(baseUrl)}`,
|
|
@@ -35,13 +39,19 @@ function buildCodexxInvocation(argv, env = process.env) {
|
|
|
35
39
|
'model_provider="hoopilot"',
|
|
36
40
|
"-c",
|
|
37
41
|
`model_providers.hoopilot=${providerConfig}`,
|
|
42
|
+
"-m",
|
|
43
|
+
model,
|
|
44
|
+
"-c",
|
|
45
|
+
`model_reasoning_effort=${JSON.stringify(reasoningEffort)}`,
|
|
38
46
|
...argv
|
|
39
47
|
],
|
|
48
|
+
baseUrl,
|
|
40
49
|
command,
|
|
41
50
|
env: withoutProxyEnv({
|
|
42
51
|
...env,
|
|
43
52
|
OPENAI_API_KEY: apiKey
|
|
44
|
-
})
|
|
53
|
+
}),
|
|
54
|
+
model
|
|
45
55
|
};
|
|
46
56
|
}
|
|
47
57
|
function withoutProxyEnv(env) {
|
|
@@ -57,6 +67,9 @@ async function main(argv = Bun.argv.slice(2), env = process.env) {
|
|
|
57
67
|
return;
|
|
58
68
|
}
|
|
59
69
|
const invocation = buildCodexxInvocation(argv, env);
|
|
70
|
+
if (env.CODEXX_SKIP_MODEL_PREFLIGHT !== "1") {
|
|
71
|
+
await verifyCodexxModel(invocation);
|
|
72
|
+
}
|
|
60
73
|
const child = spawn(invocation.command, invocation.args, {
|
|
61
74
|
env: invocation.env,
|
|
62
75
|
shell: process.platform === "win32",
|
|
@@ -74,6 +87,34 @@ async function main(argv = Bun.argv.slice(2), env = process.env) {
|
|
|
74
87
|
});
|
|
75
88
|
process.exitCode = exitCode;
|
|
76
89
|
}
|
|
90
|
+
async function verifyCodexxModel(invocation, fetcher = fetch) {
|
|
91
|
+
const modelsUrl = `${invocation.baseUrl.replace(/\/+$/, "")}/models`;
|
|
92
|
+
let response;
|
|
93
|
+
try {
|
|
94
|
+
response = await fetcher(modelsUrl, {
|
|
95
|
+
headers: {
|
|
96
|
+
accept: "application/json",
|
|
97
|
+
authorization: `Bearer ${invocation.env.OPENAI_API_KEY ?? DEFAULT_API_KEY}`
|
|
98
|
+
},
|
|
99
|
+
method: "GET"
|
|
100
|
+
});
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Could not reach Hoopilot at ${modelsUrl}. Start Hoopilot first, or set CODEXX_SKIP_MODEL_PREFLIGHT=1 to skip this check. ${errorMessage(error)}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Could not verify model ${JSON.stringify(invocation.model)} because ${modelsUrl} returned ${response.status}: ${await shortResponseText(response)}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const models = modelIds(await response.json().catch(() => void 0));
|
|
112
|
+
if (models.length > 0 && !models.includes(invocation.model)) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`The logged-in Copilot account does not advertise model ${JSON.stringify(invocation.model)} at ${modelsUrl}. Available models: ${models.join(", ")}. After upgrading Hoopilot, rerun "hoopilot login" to refresh the Copilot OAuth token, or set CODEXX_MODEL to one of the advertised model IDs.`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
77
118
|
function helpText() {
|
|
78
119
|
return `codexx
|
|
79
120
|
|
|
@@ -88,12 +129,31 @@ Environment:
|
|
|
88
129
|
HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.
|
|
89
130
|
OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.
|
|
90
131
|
CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}
|
|
132
|
+
CODEXX_MODEL Codex model to use. Default: ${DEFAULT_MODEL}
|
|
133
|
+
CODEXX_MODEL_REASONING_EFFORT
|
|
134
|
+
Codex reasoning effort. Default: ${DEFAULT_REASONING_EFFORT}
|
|
135
|
+
CODEXX_SKIP_MODEL_PREFLIGHT
|
|
136
|
+
Set to 1 to skip checking /v1/models before starting Codex.
|
|
91
137
|
|
|
92
|
-
codexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;
|
|
138
|
+
codexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, uses ${DEFAULT_MODEL} with ${DEFAULT_REASONING_EFFORT} reasoning by default, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;
|
|
93
139
|
}
|
|
94
140
|
function signalNumber(signal) {
|
|
95
141
|
return osConstants.signals[signal] ?? 1;
|
|
96
142
|
}
|
|
143
|
+
function modelIds(value) {
|
|
144
|
+
const record = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
145
|
+
const data = "data" in record && Array.isArray(record.data) ? record.data : [];
|
|
146
|
+
return data.map(
|
|
147
|
+
(entry) => entry && typeof entry === "object" && "id" in entry && typeof entry.id === "string" ? entry.id : void 0
|
|
148
|
+
).filter((id) => typeof id === "string" && id.length > 0);
|
|
149
|
+
}
|
|
150
|
+
async function shortResponseText(response) {
|
|
151
|
+
const text = await response.text();
|
|
152
|
+
return text.slice(0, 500);
|
|
153
|
+
}
|
|
154
|
+
function errorMessage(error) {
|
|
155
|
+
return error instanceof Error ? error.message : String(error);
|
|
156
|
+
}
|
|
97
157
|
if (import.meta.main) {
|
|
98
158
|
main().catch((error) => {
|
|
99
159
|
console.error(error instanceof Error ? error.message : String(error));
|
|
@@ -102,6 +162,7 @@ if (import.meta.main) {
|
|
|
102
162
|
}
|
|
103
163
|
export {
|
|
104
164
|
buildCodexxInvocation,
|
|
105
|
-
main
|
|
165
|
+
main,
|
|
166
|
+
verifyCodexxModel
|
|
106
167
|
};
|
|
107
168
|
//# sourceMappingURL=codexx.js.map
|
package/dist/codexx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/codexx.ts"],"sourcesContent":["#!/usr/bin/env bun\n\nimport { spawn } from \"node:child_process\";\nimport { constants as osConstants } from \"node:os\";\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:4141/v1\";\nconst DEFAULT_API_KEY = \"local-key\";\nconst DEFAULT_CODEX_BIN = \"codex\";\nconst PROXY_ENV_KEYS = [\n \"ALL_PROXY\",\n \"HTTPS_PROXY\",\n \"HTTP_PROXY\",\n \"NO_PROXY\",\n \"all_proxy\",\n \"https_proxy\",\n \"http_proxy\",\n \"no_proxy\",\n];\n\nexport interface CodexxInvocation {\n args: string[];\n command: string;\n env: NodeJS.ProcessEnv;\n}\n\nexport function buildCodexxInvocation(\n argv: string[],\n env: NodeJS.ProcessEnv = process.env,\n): CodexxInvocation {\n const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;\n const apiKey =\n env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;\n const command = env.CODEXX_CODEX_BIN ?? DEFAULT_CODEX_BIN;\n const providerConfig = [\n '{ name = \"Hoopilot\"',\n `base_url = ${JSON.stringify(baseUrl)}`,\n 'env_key = \"OPENAI_API_KEY\"',\n 'wire_api = \"responses\"',\n \"supports_websockets = false }\",\n ].join(\", \");\n\n return {\n args: [\n \"--disable\",\n \"network_proxy\",\n \"-c\",\n 'model_provider=\"hoopilot\"',\n \"-c\",\n `model_providers.hoopilot=${providerConfig}`,\n ...argv,\n ],\n command,\n env: withoutProxyEnv({\n ...env,\n OPENAI_API_KEY: apiKey,\n }),\n };\n}\n\nfunction withoutProxyEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const next = { ...env };\n for (const key of PROXY_ENV_KEYS) {\n delete next[key];\n }\n return next;\n}\n\nexport async function main(argv = Bun.argv.slice(2), env = process.env): Promise<void> {\n if (argv.length === 1 && (argv[0] === \"--help\" || argv[0] === \"-h\")) {\n console.log(helpText());\n return;\n }\n\n const invocation = buildCodexxInvocation(argv, env);\n const child = spawn(invocation.command, invocation.args, {\n env: invocation.env,\n shell: process.platform === \"win32\",\n stdio: \"inherit\",\n });\n\n const exitCode = await new Promise<number>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n if (typeof code === \"number\") {\n resolve(code);\n return;\n }\n resolve(signal ? 128 + signalNumber(signal) : 1);\n });\n });\n\n process.exitCode = exitCode;\n}\n\nfunction helpText(): string {\n return `codexx\n\nRun Codex against an already-running local Hoopilot server.\n\nUsage:\n codexx [codex options] [prompt]\n\nEnvironment:\n CODEXX_BASE_URL OpenAI-compatible base URL. Default: ${DEFAULT_BASE_URL}\n CODEXX_API_KEY API key sent to the local Hoopilot server.\n HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.\n OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.\n CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}\n\ncodexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;\n}\n\nfunction signalNumber(signal: NodeJS.Signals): number {\n return osConstants.signals[signal] ?? 1;\n}\n\nif (import.meta.main) {\n main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n });\n}\n"],"mappings":";;;AAEA,SAAS,aAAa;AACtB,SAAS,aAAa,mBAAmB;AAEzC,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQO,SAAS,sBACd,MACA,MAAyB,QAAQ,KACf;AAClB,QAAM,UAAU,IAAI,mBAAmB;AACvC,QAAM,SACJ,IAAI,kBAAkB,IAAI,oBAAoB,IAAI,kBAAkB;AACtE,QAAM,UAAU,IAAI,oBAAoB;AACxC,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,cAAc,KAAK,UAAU,OAAO,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,SAAO;AAAA,IACL,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,4BAA4B,cAAc;AAAA,MAC1C,GAAG;AAAA,IACL;AAAA,IACA;AAAA,IACA,KAAK,gBAAgB;AAAA,MACnB,GAAG;AAAA,MACH,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AACF;AAEA,SAAS,gBAAgB,KAA2C;AAClE,QAAM,OAAO,EAAE,GAAG,IAAI;AACtB,aAAW,OAAO,gBAAgB;AAChC,WAAO,KAAK,GAAG;AAAA,EACjB;AACA,SAAO;AACT;AAEA,eAAsB,KAAK,OAAO,IAAI,KAAK,MAAM,CAAC,GAAG,MAAM,QAAQ,KAAoB;AACrF,MAAI,KAAK,WAAW,MAAM,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,OAAO;AACnE,YAAQ,IAAI,SAAS,CAAC;AACtB;AAAA,EACF;AAEA,QAAM,aAAa,sBAAsB,MAAM,GAAG;AAClD,QAAM,QAAQ,MAAM,WAAW,SAAS,WAAW,MAAM;AAAA,IACvD,KAAK,WAAW;AAAA,IAChB,OAAO,QAAQ,aAAa;AAAA,IAC5B,OAAO;AAAA,EACT,CAAC;AAED,QAAM,WAAW,MAAM,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9D,UAAM,KAAK,SAAS,MAAM;AAC1B,UAAM,KAAK,QAAQ,CAAC,MAAM,WAAW;AACnC,UAAI,OAAO,SAAS,UAAU;AAC5B,gBAAQ,IAAI;AACZ;AAAA,MACF;AACA,cAAQ,SAAS,MAAM,aAAa,MAAM,IAAI,CAAC;AAAA,IACjD,CAAC;AAAA,EACH,CAAC;AAED,UAAQ,WAAW;AACrB;AAEA,SAAS,WAAmB;AAC1B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8DAQqD,gBAAgB;AAAA;AAAA;AAAA;AAAA,2DAInB,iBAAiB;AAAA;AAAA;AAG5E;AAEA,SAAS,aAAa,QAAgC;AACpD,SAAO,YAAY,QAAQ,MAAM,KAAK;AACxC;AAEA,IAAI,YAAY,MAAM;AACpB,OAAK,EAAE,MAAM,CAAC,UAAmB;AAC/B,YAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/codexx.ts"],"sourcesContent":["#!/usr/bin/env bun\n\nimport { spawn } from \"node:child_process\";\nimport { constants as osConstants } from \"node:os\";\nimport type { FetchLike } from \"./types\";\n\nconst DEFAULT_BASE_URL = \"http://127.0.0.1:4141/v1\";\nconst DEFAULT_API_KEY = \"local-key\";\nconst DEFAULT_CODEX_BIN = \"codex\";\nconst DEFAULT_MODEL = \"gpt-5.5\";\nconst DEFAULT_REASONING_EFFORT = \"xhigh\";\nconst PROXY_ENV_KEYS = [\n \"ALL_PROXY\",\n \"HTTPS_PROXY\",\n \"HTTP_PROXY\",\n \"NO_PROXY\",\n \"all_proxy\",\n \"https_proxy\",\n \"http_proxy\",\n \"no_proxy\",\n];\n\nexport interface CodexxInvocation {\n args: string[];\n baseUrl: string;\n command: string;\n env: NodeJS.ProcessEnv;\n model: string;\n}\n\nexport function buildCodexxInvocation(\n argv: string[],\n env: NodeJS.ProcessEnv = process.env,\n): CodexxInvocation {\n const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;\n const apiKey =\n env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;\n const command = env.CODEXX_CODEX_BIN ?? DEFAULT_CODEX_BIN;\n const model = env.CODEXX_MODEL ?? DEFAULT_MODEL;\n const reasoningEffort = env.CODEXX_MODEL_REASONING_EFFORT ?? DEFAULT_REASONING_EFFORT;\n const providerConfig = [\n '{ name = \"Hoopilot\"',\n `base_url = ${JSON.stringify(baseUrl)}`,\n 'env_key = \"OPENAI_API_KEY\"',\n 'wire_api = \"responses\"',\n \"supports_websockets = false }\",\n ].join(\", \");\n\n return {\n args: [\n \"--disable\",\n \"network_proxy\",\n \"-c\",\n 'model_provider=\"hoopilot\"',\n \"-c\",\n `model_providers.hoopilot=${providerConfig}`,\n \"-m\",\n model,\n \"-c\",\n `model_reasoning_effort=${JSON.stringify(reasoningEffort)}`,\n ...argv,\n ],\n baseUrl,\n command,\n env: withoutProxyEnv({\n ...env,\n OPENAI_API_KEY: apiKey,\n }),\n model,\n };\n}\n\nfunction withoutProxyEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n const next = { ...env };\n for (const key of PROXY_ENV_KEYS) {\n delete next[key];\n }\n return next;\n}\n\nexport async function main(argv = Bun.argv.slice(2), env = process.env): Promise<void> {\n if (argv.length === 1 && (argv[0] === \"--help\" || argv[0] === \"-h\")) {\n console.log(helpText());\n return;\n }\n\n const invocation = buildCodexxInvocation(argv, env);\n if (env.CODEXX_SKIP_MODEL_PREFLIGHT !== \"1\") {\n await verifyCodexxModel(invocation);\n }\n const child = spawn(invocation.command, invocation.args, {\n env: invocation.env,\n shell: process.platform === \"win32\",\n stdio: \"inherit\",\n });\n\n const exitCode = await new Promise<number>((resolve, reject) => {\n child.once(\"error\", reject);\n child.once(\"exit\", (code, signal) => {\n if (typeof code === \"number\") {\n resolve(code);\n return;\n }\n resolve(signal ? 128 + signalNumber(signal) : 1);\n });\n });\n\n process.exitCode = exitCode;\n}\n\nexport async function verifyCodexxModel(\n invocation: Pick<CodexxInvocation, \"baseUrl\" | \"env\" | \"model\">,\n fetcher: FetchLike = fetch,\n): Promise<void> {\n const modelsUrl = `${invocation.baseUrl.replace(/\\/+$/, \"\")}/models`;\n let response: Response;\n try {\n response = await fetcher(modelsUrl, {\n headers: {\n accept: \"application/json\",\n authorization: `Bearer ${invocation.env.OPENAI_API_KEY ?? DEFAULT_API_KEY}`,\n },\n method: \"GET\",\n });\n } catch (error) {\n throw new Error(\n `Could not reach Hoopilot at ${modelsUrl}. Start Hoopilot first, or set CODEXX_SKIP_MODEL_PREFLIGHT=1 to skip this check. ${errorMessage(error)}`,\n );\n }\n\n if (!response.ok) {\n throw new Error(\n `Could not verify model ${JSON.stringify(invocation.model)} because ${modelsUrl} returned ${response.status}: ${await shortResponseText(response)}`,\n );\n }\n\n const models = modelIds(await response.json().catch(() => undefined));\n if (models.length > 0 && !models.includes(invocation.model)) {\n throw new Error(\n `The logged-in Copilot account does not advertise model ${JSON.stringify(invocation.model)} at ${modelsUrl}. Available models: ${models.join(\", \")}. After upgrading Hoopilot, rerun \"hoopilot login\" to refresh the Copilot OAuth token, or set CODEXX_MODEL to one of the advertised model IDs.`,\n );\n }\n}\n\nfunction helpText(): string {\n return `codexx\n\nRun Codex against an already-running local Hoopilot server.\n\nUsage:\n codexx [codex options] [prompt]\n\nEnvironment:\n CODEXX_BASE_URL OpenAI-compatible base URL. Default: ${DEFAULT_BASE_URL}\n CODEXX_API_KEY API key sent to the local Hoopilot server.\n HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.\n OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.\n CODEXX_CODEX_BIN Codex executable to run. Default: ${DEFAULT_CODEX_BIN}\n CODEXX_MODEL Codex model to use. Default: ${DEFAULT_MODEL}\n CODEXX_MODEL_REASONING_EFFORT\n Codex reasoning effort. Default: ${DEFAULT_REASONING_EFFORT}\n CODEXX_SKIP_MODEL_PREFLIGHT\n Set to 1 to skip checking /v1/models before starting Codex.\n\ncodexx does not start Hoopilot and does not change your shell environment. It selects a temporary Hoopilot model provider with Responses WebSockets disabled, uses ${DEFAULT_MODEL} with ${DEFAULT_REASONING_EFFORT} reasoning by default, disables Codex's network_proxy feature, and removes proxy variables only from the spawned Codex process.`;\n}\n\nfunction signalNumber(signal: NodeJS.Signals): number {\n return osConstants.signals[signal] ?? 1;\n}\n\nfunction modelIds(value: unknown): string[] {\n const record = value && typeof value === \"object\" && !Array.isArray(value) ? value : {};\n const data = \"data\" in record && Array.isArray(record.data) ? record.data : [];\n return data\n .map((entry) =>\n entry && typeof entry === \"object\" && \"id\" in entry && typeof entry.id === \"string\"\n ? entry.id\n : undefined,\n )\n .filter((id): id is string => typeof id === \"string\" && id.length > 0);\n}\n\nasync function shortResponseText(response: Response): Promise<string> {\n const text = await response.text();\n return text.slice(0, 500);\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nif (import.meta.main) {\n main().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -174,6 +174,16 @@ var CopilotClient = class {
|
|
|
174
174
|
signal
|
|
175
175
|
});
|
|
176
176
|
}
|
|
177
|
+
async responses(body, signal) {
|
|
178
|
+
return this.fetchCopilot("/responses", {
|
|
179
|
+
body,
|
|
180
|
+
headers: {
|
|
181
|
+
"content-type": "application/json"
|
|
182
|
+
},
|
|
183
|
+
method: "POST",
|
|
184
|
+
signal
|
|
185
|
+
});
|
|
186
|
+
}
|
|
177
187
|
async models(signal) {
|
|
178
188
|
return this.fetchCopilot("/models", {
|
|
179
189
|
headers: {
|
|
@@ -203,7 +213,7 @@ var CopilotClient = class {
|
|
|
203
213
|
|
|
204
214
|
// src/github-device.ts
|
|
205
215
|
var import_promises = require("timers/promises");
|
|
206
|
-
var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "
|
|
216
|
+
var DEFAULT_GITHUB_COPILOT_CLIENT_ID = "Ov23li8tweQw6odWQebz";
|
|
207
217
|
var DEFAULT_GITHUB_DOMAIN = "github.com";
|
|
208
218
|
var DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
209
219
|
var POLLING_SAFETY_MARGIN_MS = 3e3;
|
|
@@ -423,10 +433,6 @@ function isLogLevel(value) {
|
|
|
423
433
|
|
|
424
434
|
// src/openai.ts
|
|
425
435
|
var DEFAULT_MODEL = "gpt-4.1";
|
|
426
|
-
var MODEL_ALIASES = {
|
|
427
|
-
"gpt-5.5": DEFAULT_MODEL,
|
|
428
|
-
"gpt-5.5-codex": DEFAULT_MODEL
|
|
429
|
-
};
|
|
430
436
|
function responsesRequestToChatCompletion(request) {
|
|
431
437
|
const messages = [];
|
|
432
438
|
const instructions = contentToText(request.instructions);
|
|
@@ -471,10 +477,7 @@ function completionsRequestToChatCompletion(request) {
|
|
|
471
477
|
}
|
|
472
478
|
function normalizeRequestedModel(model) {
|
|
473
479
|
const requested = contentToText(model).trim();
|
|
474
|
-
|
|
475
|
-
return DEFAULT_MODEL;
|
|
476
|
-
}
|
|
477
|
-
return MODEL_ALIASES[requested.toLowerCase()] ?? requested;
|
|
480
|
+
return requested || DEFAULT_MODEL;
|
|
478
481
|
}
|
|
479
482
|
function chatCompletionToResponse(completion, responseId) {
|
|
480
483
|
const id = responseId ?? `resp_${randomId()}`;
|
|
@@ -1125,8 +1128,7 @@ async function handleModels(client, signal, logger) {
|
|
|
1125
1128
|
}
|
|
1126
1129
|
async function handleChatCompletions(client, request, logger) {
|
|
1127
1130
|
const chatRequest = normalizeChatCompletionRequest(await readJson(request));
|
|
1128
|
-
const
|
|
1129
|
-
const upstream = result.upstream;
|
|
1131
|
+
const upstream = await client.chatCompletions(chatRequest, request.signal);
|
|
1130
1132
|
if (!upstream.ok) {
|
|
1131
1133
|
return proxyError(upstream, logger);
|
|
1132
1134
|
}
|
|
@@ -1135,13 +1137,10 @@ async function handleChatCompletions(client, request, logger) {
|
|
|
1135
1137
|
}
|
|
1136
1138
|
async function handleCompletions(client, request, logger) {
|
|
1137
1139
|
const body = await readJson(request);
|
|
1138
|
-
const
|
|
1139
|
-
client,
|
|
1140
|
+
const upstream = await client.chatCompletions(
|
|
1140
1141
|
completionsRequestToChatCompletion(body),
|
|
1141
|
-
request.signal
|
|
1142
|
-
logger
|
|
1142
|
+
request.signal
|
|
1143
1143
|
);
|
|
1144
|
-
const upstream = result.upstream;
|
|
1145
1144
|
if (!upstream.ok) {
|
|
1146
1145
|
return proxyError(upstream, logger);
|
|
1147
1146
|
}
|
|
@@ -1149,76 +1148,13 @@ async function handleCompletions(client, request, logger) {
|
|
|
1149
1148
|
return jsonResponse(chatCompletionToCompletion(await upstream.json()));
|
|
1150
1149
|
}
|
|
1151
1150
|
async function handleResponses(client, request, logger) {
|
|
1152
|
-
const body = await
|
|
1153
|
-
const
|
|
1154
|
-
const result = await sendChatCompletions(client, chatRequest, request.signal, logger);
|
|
1155
|
-
const upstream = result.upstream;
|
|
1151
|
+
const body = await readJsonText(request);
|
|
1152
|
+
const upstream = await client.responses(body, request.signal);
|
|
1156
1153
|
if (!upstream.ok) {
|
|
1157
1154
|
return proxyError(upstream, logger);
|
|
1158
1155
|
}
|
|
1159
|
-
logUpstreamSuccess(logger, "/
|
|
1160
|
-
|
|
1161
|
-
return new Response(
|
|
1162
|
-
responsesStreamFromChatStream(upstream.body, {
|
|
1163
|
-
model: result.model
|
|
1164
|
-
}),
|
|
1165
|
-
{
|
|
1166
|
-
headers: {
|
|
1167
|
-
...corsHeaders(),
|
|
1168
|
-
"cache-control": "no-cache",
|
|
1169
|
-
connection: "keep-alive",
|
|
1170
|
-
"content-type": "text/event-stream; charset=utf-8"
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
);
|
|
1174
|
-
}
|
|
1175
|
-
return jsonResponse(chatCompletionToResponse(await upstream.json()));
|
|
1176
|
-
}
|
|
1177
|
-
async function sendChatCompletions(client, chatRequest, signal, logger) {
|
|
1178
|
-
const model = requestModel(chatRequest);
|
|
1179
|
-
const upstream = await client.chatCompletions(chatRequest, signal);
|
|
1180
|
-
if (upstream.ok || isUpstreamAuthStatus(upstream.status)) {
|
|
1181
|
-
return { model, upstream };
|
|
1182
|
-
}
|
|
1183
|
-
const text = await upstream.text();
|
|
1184
|
-
if (!shouldRetryWithDefaultModel(upstream.status, text, model)) {
|
|
1185
|
-
return { model, upstream: textResponse(upstream, text) };
|
|
1186
|
-
}
|
|
1187
|
-
logger.warn(
|
|
1188
|
-
{
|
|
1189
|
-
event: "copilot.model.fallback",
|
|
1190
|
-
fallbackModel: DEFAULT_MODEL,
|
|
1191
|
-
upstreamPath: "/chat/completions",
|
|
1192
|
-
upstreamStatus: upstream.status
|
|
1193
|
-
},
|
|
1194
|
-
"retrying chat completion with fallback model"
|
|
1195
|
-
);
|
|
1196
|
-
return {
|
|
1197
|
-
model: DEFAULT_MODEL,
|
|
1198
|
-
upstream: await client.chatCompletions({ ...chatRequest, model: DEFAULT_MODEL }, signal)
|
|
1199
|
-
};
|
|
1200
|
-
}
|
|
1201
|
-
function shouldRetryWithDefaultModel(status, text, model) {
|
|
1202
|
-
if (model === DEFAULT_MODEL || status < 400 || status >= 500) {
|
|
1203
|
-
return false;
|
|
1204
|
-
}
|
|
1205
|
-
const normalized = text.toLowerCase();
|
|
1206
|
-
return normalized.includes("model") && (normalized.includes("not supported") || normalized.includes("unsupported") || normalized.includes("invalid model"));
|
|
1207
|
-
}
|
|
1208
|
-
function requestModel(request) {
|
|
1209
|
-
return typeof request.model === "string" && request.model.trim() ? request.model : DEFAULT_MODEL;
|
|
1210
|
-
}
|
|
1211
|
-
function textResponse(upstream, text) {
|
|
1212
|
-
const headers = new Headers();
|
|
1213
|
-
const contentType = upstream.headers.get("content-type");
|
|
1214
|
-
if (contentType) {
|
|
1215
|
-
headers.set("content-type", contentType);
|
|
1216
|
-
}
|
|
1217
|
-
return new Response(text, {
|
|
1218
|
-
headers,
|
|
1219
|
-
status: upstream.status,
|
|
1220
|
-
statusText: upstream.statusText
|
|
1221
|
-
});
|
|
1156
|
+
logUpstreamSuccess(logger, "/responses", upstream.status);
|
|
1157
|
+
return proxyResponse(upstream);
|
|
1222
1158
|
}
|
|
1223
1159
|
async function proxyError(upstream, logger) {
|
|
1224
1160
|
const text = await upstream.text();
|
|
@@ -1257,6 +1193,15 @@ async function readJson(request) {
|
|
|
1257
1193
|
throw new Error(INVALID_JSON_MESSAGE);
|
|
1258
1194
|
}
|
|
1259
1195
|
}
|
|
1196
|
+
async function readJsonText(request) {
|
|
1197
|
+
const text = await request.text();
|
|
1198
|
+
try {
|
|
1199
|
+
JSON.parse(text);
|
|
1200
|
+
return text;
|
|
1201
|
+
} catch {
|
|
1202
|
+
throw new Error(INVALID_JSON_MESSAGE);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1260
1205
|
function jsonResponse(body, status = 200) {
|
|
1261
1206
|
return new Response(JSON.stringify(body), {
|
|
1262
1207
|
headers: {
|