@openhoo/hoopilot 0.7.1 → 0.7.3

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/dist/codexx.js CHANGED
@@ -1,165 +1,9 @@
1
1
  #!/usr/bin/env bun
2
-
3
- // src/codexx.ts
4
- import { spawn } from "child_process";
5
- import { constants as osConstants } from "os";
6
- var DEFAULT_BASE_URL = "http://127.0.0.1:4141/v1";
7
- var DEFAULT_API_KEY = "local-key";
8
- var DEFAULT_CODEX_BIN = "codex";
9
- var DEFAULT_MODEL = "gpt-5.5";
10
- var DEFAULT_REASONING_EFFORT = "xhigh";
11
- var PROXY_ENV_KEYS = [
12
- "ALL_PROXY",
13
- "HTTPS_PROXY",
14
- "HTTP_PROXY",
15
- "NO_PROXY",
16
- "all_proxy",
17
- "https_proxy",
18
- "http_proxy",
19
- "no_proxy"
20
- ];
21
- function buildCodexxInvocation(argv, env = process.env) {
22
- const baseUrl = env.CODEXX_BASE_URL ?? DEFAULT_BASE_URL;
23
- const apiKey = env.CODEXX_API_KEY ?? env.HOOPILOT_API_KEY ?? env.OPENAI_API_KEY ?? DEFAULT_API_KEY;
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;
27
- const providerConfig = [
28
- '{ name = "Hoopilot"',
29
- `base_url = ${JSON.stringify(baseUrl)}`,
30
- 'env_key = "OPENAI_API_KEY"',
31
- 'wire_api = "responses"',
32
- "supports_websockets = false }"
33
- ].join(", ");
34
- return {
35
- args: [
36
- "--disable",
37
- "network_proxy",
38
- "-c",
39
- 'model_provider="hoopilot"',
40
- "-c",
41
- `model_providers.hoopilot=${providerConfig}`,
42
- "-m",
43
- model,
44
- "-c",
45
- `model_reasoning_effort=${JSON.stringify(reasoningEffort)}`,
46
- ...argv
47
- ],
48
- baseUrl,
49
- command,
50
- env: withoutProxyEnv({
51
- ...env,
52
- OPENAI_API_KEY: apiKey
53
- }),
54
- model
55
- };
56
- }
57
- function withoutProxyEnv(env) {
58
- const next = { ...env };
59
- for (const key of PROXY_ENV_KEYS) {
60
- delete next[key];
61
- }
62
- return next;
63
- }
64
- async function main(argv = Bun.argv.slice(2), env = process.env) {
65
- if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) {
66
- console.log(helpText());
67
- return;
68
- }
69
- const invocation = buildCodexxInvocation(argv, env);
70
- if (env.CODEXX_SKIP_MODEL_PREFLIGHT !== "1") {
71
- await verifyCodexxModel(invocation);
72
- }
73
- const child = spawn(invocation.command, invocation.args, {
74
- env: invocation.env,
75
- shell: process.platform === "win32",
76
- stdio: "inherit"
77
- });
78
- const exitCode = await new Promise((resolve, reject) => {
79
- child.once("error", reject);
80
- child.once("exit", (code, signal) => {
81
- if (typeof code === "number") {
82
- resolve(code);
83
- return;
84
- }
85
- resolve(signal ? 128 + signalNumber(signal) : 1);
86
- });
87
- });
88
- process.exitCode = exitCode;
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
- }
118
- function helpText() {
119
- return `codexx
120
-
121
- Run Codex against an already-running local Hoopilot server.
122
-
123
- Usage:
124
- codexx [codex options] [prompt]
125
-
126
- Environment:
127
- CODEXX_BASE_URL OpenAI-compatible base URL. Default: ${DEFAULT_BASE_URL}
128
- CODEXX_API_KEY API key sent to the local Hoopilot server.
129
- HOOPILOT_API_KEY Used as the API key when CODEXX_API_KEY is unset.
130
- OPENAI_API_KEY Used as the API key when both CODEXX_API_KEY and HOOPILOT_API_KEY are unset.
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.
137
-
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.`;
139
- }
140
- function signalNumber(signal) {
141
- return osConstants.signals[signal] ?? 1;
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
- }
157
- if (import.meta.main) {
158
- main().catch((error) => {
159
- console.error(errorMessage(error));
160
- process.exit(1);
161
- });
162
- }
2
+ import {
3
+ buildCodexxInvocation,
4
+ main,
5
+ verifyCodexxModel
6
+ } from "./chunk-7GSQVYYT.js";
163
7
  export {
164
8
  buildCodexxInvocation,
165
9
  main,
@@ -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\";\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(errorMessage(error));\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}