@minniexcode/codex-switch 0.2.0 → 0.2.2

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.
Files changed (40) hide show
  1. package/README.AI.md +66 -94
  2. package/README.CN.md +84 -139
  3. package/README.md +91 -151
  4. package/dist/app/add-provider.js +6 -83
  5. package/dist/app/edit-provider.js +9 -29
  6. package/dist/app/get-status.js +1 -77
  7. package/dist/app/list-providers.js +0 -2
  8. package/dist/app/remove-provider.js +3 -5
  9. package/dist/app/run-doctor.js +2 -99
  10. package/dist/app/setup-codex.js +0 -2
  11. package/dist/app/switch-provider.js +1 -74
  12. package/dist/cli/output.js +3 -89
  13. package/dist/commands/handlers.js +20 -172
  14. package/dist/commands/help.js +1 -4
  15. package/dist/commands/registry.js +6 -74
  16. package/dist/domain/config.js +1 -3
  17. package/dist/domain/providers.js +4 -92
  18. package/dist/domain/runtime-state.js +0 -88
  19. package/dist/interaction/add-interactive.js +1 -55
  20. package/dist/interaction/interactive.js +1 -3
  21. package/dist/runtime/codex-probe.js +0 -12
  22. package/dist/storage/codex-paths.js +0 -2
  23. package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
  24. package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
  25. package/docs/Tests/testing.md +32 -34
  26. package/docs/cli-usage.md +67 -235
  27. package/docs/codex-switch-command-design.md +1 -1
  28. package/docs/codex-switch-product-overview.md +49 -96
  29. package/docs/codex-switch-technical-architecture.md +37 -52
  30. package/package.json +1 -1
  31. package/dist/app/bridge.js +0 -303
  32. package/dist/runtime/copilot-adapter.js +0 -617
  33. package/dist/runtime/copilot-bridge-worker.js +0 -69
  34. package/dist/runtime/copilot-bridge.js +0 -1351
  35. package/dist/runtime/copilot-cli.js +0 -164
  36. package/dist/runtime/copilot-http-bridge-worker.js +0 -228
  37. package/dist/runtime/copilot-installer.js +0 -231
  38. package/dist/runtime/copilot-sdk-loader.js +0 -62
  39. package/dist/runtime/copilot-token.js +0 -294
  40. package/dist/storage/runtime-state-repo.js +0 -121
@@ -1,164 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.setCopilotCliSpawnImplementation = setCopilotCliSpawnImplementation;
37
- exports.resetCopilotCliSpawnImplementation = resetCopilotCliSpawnImplementation;
38
- exports.checkCopilotCliAvailable = checkCopilotCliAvailable;
39
- exports.resolveCopilotCliInvocation = resolveCopilotCliInvocation;
40
- exports.resolveCopilotSdkRuntimeInvocation = resolveCopilotSdkRuntimeInvocation;
41
- exports.runCopilotLogin = runCopilotLogin;
42
- const fs = __importStar(require("node:fs"));
43
- const path = __importStar(require("node:path"));
44
- const node_child_process_1 = require("node:child_process");
45
- const copilot_installer_1 = require("./copilot-installer");
46
- let spawnImplementation = node_child_process_1.spawnSync;
47
- /**
48
- * Overrides the spawn implementation for Copilot CLI tests.
49
- */
50
- function setCopilotCliSpawnImplementation(spawnLike) {
51
- spawnImplementation = spawnLike;
52
- }
53
- /**
54
- * Restores the default spawn implementation after tests.
55
- */
56
- function resetCopilotCliSpawnImplementation() {
57
- spawnImplementation = node_child_process_1.spawnSync;
58
- }
59
- /**
60
- * Checks whether the GitHub Copilot CLI is available either from the bundled runtime or on PATH.
61
- */
62
- function checkCopilotCliAvailable(runtimesDir) {
63
- const invocation = getCopilotInvocation(["--help"], runtimesDir);
64
- const result = spawnImplementation(invocation.command, invocation.args, {
65
- stdio: "pipe",
66
- encoding: "utf8",
67
- shell: invocation.shell,
68
- });
69
- if (result.error || result.status !== 0) {
70
- return {
71
- ok: false,
72
- cause: result.error?.message ?? (result.stderr.trim() || "Unknown failure"),
73
- source: invocation.source,
74
- command: formatInvocation(invocation),
75
- };
76
- }
77
- return {
78
- ok: true,
79
- source: invocation.source,
80
- command: formatInvocation(invocation),
81
- };
82
- }
83
- /**
84
- * Resolves the Copilot CLI invocation used by SDK clients and command probes.
85
- */
86
- function resolveCopilotCliInvocation(args = [], runtimesDir) {
87
- return getCopilotInvocation(args, runtimesDir);
88
- }
89
- /**
90
- * Resolves the explicit runtime entrypoint required by the Copilot SDK.
91
- */
92
- function resolveCopilotSdkRuntimeInvocation(runtimesDir) {
93
- const installDir = (0, copilot_installer_1.getCopilotRuntimeInstallDir)(runtimesDir);
94
- const loaderPath = path.join(installDir, "node_modules", "@github", "copilot", "npm-loader.js");
95
- if (!fs.existsSync(loaderPath)) {
96
- return null;
97
- }
98
- return {
99
- path: loaderPath,
100
- args: [],
101
- };
102
- }
103
- /**
104
- * Launches the official `copilot login` flow in the current terminal.
105
- */
106
- function runCopilotLogin(options) {
107
- const args = ["login"];
108
- if (options?.host) {
109
- args.push("--hostname", options.host);
110
- }
111
- const invocation = getCopilotInvocation(args, options?.runtimesDir);
112
- const result = spawnImplementation(invocation.command, invocation.args, {
113
- stdio: "inherit",
114
- shell: invocation.shell,
115
- });
116
- if (result.error || result.status !== 0) {
117
- throw new Error(result.error?.message ??
118
- `${formatInvocation(invocation)} exited with status ${String(result.status)}`);
119
- }
120
- }
121
- /**
122
- * Resolves a cross-platform invocation for the Copilot CLI.
123
- */
124
- function getCopilotInvocation(args, runtimesDir) {
125
- const bundledCommand = resolveBundledCopilotCommand(runtimesDir);
126
- const executable = bundledCommand ?? "copilot";
127
- if (process.platform === "win32") {
128
- return {
129
- command: executable,
130
- args,
131
- source: bundledCommand ? "bundled" : "path",
132
- shell: true,
133
- };
134
- }
135
- return {
136
- command: executable,
137
- args,
138
- source: bundledCommand ? "bundled" : "path",
139
- shell: false,
140
- };
141
- }
142
- /**
143
- * Resolves the bundled Copilot CLI shim installed alongside the optional runtime.
144
- */
145
- function resolveBundledCopilotCommand(runtimesDir) {
146
- const installDir = (0, copilot_installer_1.getCopilotRuntimeInstallDir)(runtimesDir);
147
- const candidates = process.platform === "win32"
148
- ? [path.join(installDir, "node_modules", ".bin", "copilot.cmd")]
149
- : [path.join(installDir, "node_modules", ".bin", "copilot")];
150
- for (const candidate of candidates) {
151
- if (fs.existsSync(candidate)) {
152
- return candidate;
153
- }
154
- }
155
- return null;
156
- }
157
- /**
158
- * Renders the invocation into a short human-readable string for diagnostics.
159
- */
160
- function formatInvocation(invocation) {
161
- return invocation.command === "copilot"
162
- ? ["copilot", ...invocation.args].join(" ")
163
- : [invocation.command, ...invocation.args].join(" ");
164
- }
@@ -1,228 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- const http = __importStar(require("node:http"));
37
- const https = __importStar(require("node:https"));
38
- const crypto = __importStar(require("node:crypto"));
39
- const copilot_token_1 = require("./copilot-token");
40
- let tokenManager = null;
41
- function logWorkerEvent(message) {
42
- process.stderr.write(`[${new Date().toISOString()}] ${message}\n`);
43
- }
44
- async function main() {
45
- const provider = process.env.CODEX_SWITCH_BRIDGE_PROVIDER ?? "copilot";
46
- const host = process.env.CODEX_SWITCH_BRIDGE_HOST ?? "127.0.0.1";
47
- const port = Number(process.env.CODEX_SWITCH_BRIDGE_PORT ?? "41415");
48
- const localApiKey = process.env.CODEX_SWITCH_BRIDGE_API_KEY ?? "";
49
- const toolHomeDir = process.env.CODEX_SWITCH_TOOL_HOME_DIR || undefined;
50
- const staticCopilotToken = process.env.CODEX_SWITCH_BRIDGE_COPILOT_TOKEN || undefined;
51
- logWorkerEvent(`worker startup provider=${provider} host=${host} port=${String(port)}`);
52
- if (staticCopilotToken) {
53
- tokenManager = (0, copilot_token_1.createStaticTokenManager)(staticCopilotToken);
54
- logWorkerEvent("copilot token acquired (static), api base: https://api.githubcopilot.com");
55
- }
56
- else {
57
- const githubPat = process.env.CODEX_SWITCH_GITHUB_TOKEN || (0, copilot_token_1.readGithubToken)(toolHomeDir);
58
- if (!githubPat) {
59
- throw new Error("No GitHub token found. Run `codexs login copilot` first.");
60
- }
61
- tokenManager = (0, copilot_token_1.createTokenManager)(githubPat);
62
- await tokenManager.getToken();
63
- logWorkerEvent(`copilot token acquired, api base: ${tokenManager.getApiBaseUrl()}`);
64
- }
65
- const server = http.createServer(async (req, res) => {
66
- try {
67
- await handleRequest(req, res, localApiKey);
68
- }
69
- catch (error) {
70
- if (!res.headersSent) {
71
- res.writeHead(500, { "content-type": "application/json" });
72
- res.end(JSON.stringify({ error: { message: error instanceof Error ? error.message : String(error) } }));
73
- }
74
- }
75
- });
76
- const stopWorker = () => {
77
- logWorkerEvent(`worker shutdown provider=${provider}`);
78
- tokenManager?.stop();
79
- server.close();
80
- process.exit(0);
81
- };
82
- process.once("SIGINT", stopWorker);
83
- process.once("SIGTERM", stopWorker);
84
- await new Promise((resolve, reject) => {
85
- server.once("error", reject);
86
- server.listen(port, host, () => {
87
- server.off("error", reject);
88
- resolve();
89
- });
90
- });
91
- logWorkerEvent(`worker ready provider=${provider} host=${host} port=${String(port)}`);
92
- }
93
- async function handleRequest(req, res, localApiKey) {
94
- const method = req.method ?? "GET";
95
- const url = req.url ?? "/";
96
- if (method === "GET" && url === "/healthz") {
97
- res.writeHead(200, { "content-type": "application/json" });
98
- res.end(JSON.stringify({ ok: true }));
99
- return;
100
- }
101
- if (!isAuthorized(req, localApiKey)) {
102
- res.writeHead(401, { "content-type": "application/json" });
103
- res.end(JSON.stringify({ error: { message: "Unauthorized" } }));
104
- return;
105
- }
106
- if (method === "GET" && url === "/v1/models") {
107
- await proxyGet("/models", res);
108
- return;
109
- }
110
- if (method === "POST" && url === "/v1/chat/completions") {
111
- await proxyPost("/chat/completions", req, res);
112
- return;
113
- }
114
- if (method === "POST" && url === "/v1/responses") {
115
- await proxyPost("/responses", req, res);
116
- return;
117
- }
118
- res.writeHead(404, { "content-type": "application/json" });
119
- res.end(JSON.stringify({ error: { message: "Not found" } }));
120
- }
121
- async function proxyGet(upstreamPath, res) {
122
- const copilotToken = await tokenManager.getToken();
123
- const apiBase = tokenManager.getApiBaseUrl();
124
- const targetUrl = new URL(upstreamPath, apiBase);
125
- const headers = (0, copilot_token_1.getCopilotRequestHeaders)(copilotToken);
126
- const upstreamRes = await httpsRequest({
127
- method: "GET",
128
- url: targetUrl,
129
- headers,
130
- });
131
- res.writeHead(upstreamRes.statusCode, filterResponseHeaders(upstreamRes.headers));
132
- upstreamRes.pipe(res);
133
- }
134
- async function proxyPost(upstreamPath, req, res) {
135
- const body = await readRequestBody(req);
136
- const copilotToken = await tokenManager.getToken();
137
- const apiBase = tokenManager.getApiBaseUrl();
138
- const targetUrl = new URL(upstreamPath, apiBase);
139
- const requestId = crypto.randomUUID();
140
- const headers = (0, copilot_token_1.getCopilotRequestHeaders)(copilotToken, requestId);
141
- // Preserve content-length for the body
142
- headers["content-length"] = Buffer.byteLength(body).toString();
143
- const upstreamRes = await httpsRequest({
144
- method: "POST",
145
- url: targetUrl,
146
- headers,
147
- body,
148
- });
149
- // If upstream returned 401, try refreshing token once and retry
150
- if (upstreamRes.statusCode === 401) {
151
- upstreamRes.resume();
152
- logWorkerEvent("upstream 401, invalidating token and retrying");
153
- tokenManager.invalidate();
154
- const freshToken = await tokenManager.getToken();
155
- const retryHeaders = (0, copilot_token_1.getCopilotRequestHeaders)(freshToken, crypto.randomUUID());
156
- retryHeaders["content-length"] = Buffer.byteLength(body).toString();
157
- const retryRes = await httpsRequest({
158
- method: "POST",
159
- url: targetUrl,
160
- headers: retryHeaders,
161
- body,
162
- });
163
- res.writeHead(retryRes.statusCode, filterResponseHeaders(retryRes.headers));
164
- retryRes.pipe(res);
165
- return;
166
- }
167
- res.writeHead(upstreamRes.statusCode, filterResponseHeaders(upstreamRes.headers));
168
- upstreamRes.pipe(res);
169
- }
170
- function httpsRequest(args) {
171
- return new Promise((resolve, reject) => {
172
- const req = https.request({
173
- hostname: args.url.hostname,
174
- port: args.url.port || 443,
175
- path: args.url.pathname + args.url.search,
176
- method: args.method,
177
- headers: args.headers,
178
- }, (res) => {
179
- resolve(res);
180
- });
181
- req.on("error", reject);
182
- if (args.body) {
183
- req.write(args.body);
184
- }
185
- req.end();
186
- });
187
- }
188
- function readRequestBody(req) {
189
- return new Promise((resolve, reject) => {
190
- const chunks = [];
191
- req.on("data", (chunk) => chunks.push(chunk));
192
- req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
193
- req.on("error", reject);
194
- });
195
- }
196
- function filterResponseHeaders(headers) {
197
- const filtered = {};
198
- const passthrough = ["content-type", "transfer-encoding", "x-request-id"];
199
- for (const key of passthrough) {
200
- if (headers[key]) {
201
- filtered[key] = headers[key];
202
- }
203
- }
204
- // Always allow cache-control for SSE
205
- if (headers["cache-control"]) {
206
- filtered["cache-control"] = headers["cache-control"];
207
- }
208
- return filtered;
209
- }
210
- function isAuthorized(req, expectedApiKey) {
211
- const authorization = req.headers.authorization;
212
- if (!authorization || !authorization.startsWith("Bearer ")) {
213
- return false;
214
- }
215
- return authorization.slice("Bearer ".length) === expectedApiKey;
216
- }
217
- if (require.main === module) {
218
- process.on("uncaughtException", (error) => {
219
- logWorkerEvent(`worker uncaught exception: ${error.message}`);
220
- });
221
- process.on("unhandledRejection", (reason) => {
222
- logWorkerEvent(`worker unhandled rejection: ${reason instanceof Error ? reason.message : String(reason)}`);
223
- });
224
- void main().catch((error) => {
225
- logWorkerEvent(`worker startup failure: ${error instanceof Error ? error.message : String(error)}`);
226
- process.exit(1);
227
- });
228
- }
@@ -1,231 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.setCopilotInstallerSpawnImplementation = setCopilotInstallerSpawnImplementation;
37
- exports.resetCopilotInstallerSpawnImplementation = resetCopilotInstallerSpawnImplementation;
38
- exports.getCopilotRuntimeInstallDir = getCopilotRuntimeInstallDir;
39
- exports.getCopilotSdkPackageName = getCopilotSdkPackageName;
40
- exports.getSupportedCopilotSdkVersion = getSupportedCopilotSdkVersion;
41
- exports.getCopilotNodeRuntimeStatus = getCopilotNodeRuntimeStatus;
42
- exports.assertCopilotNodeRuntimeSupported = assertCopilotNodeRuntimeSupported;
43
- exports.isSupportedCopilotSdkVersion = isSupportedCopilotSdkVersion;
44
- exports.probeCopilotSdkInstall = probeCopilotSdkInstall;
45
- exports.installCopilotSdk = installCopilotSdk;
46
- const fs = __importStar(require("node:fs"));
47
- const path = __importStar(require("node:path"));
48
- const node_child_process_1 = require("node:child_process");
49
- const errors_1 = require("../domain/errors");
50
- const codex_paths_1 = require("../storage/codex-paths");
51
- const COPILOT_SDK_PACKAGE = "@github/copilot-sdk";
52
- const COPILOT_SDK_VERSION = "1.0.2";
53
- const COPILOT_MIN_NODE_MAJOR = 20;
54
- let spawnImplementation = node_child_process_1.spawnSync;
55
- /**
56
- * Overrides the spawn implementation for runtime installer tests.
57
- */
58
- function setCopilotInstallerSpawnImplementation(spawnLike) {
59
- spawnImplementation = spawnLike;
60
- }
61
- /**
62
- * Restores the default spawn implementation after tests.
63
- */
64
- function resetCopilotInstallerSpawnImplementation() {
65
- spawnImplementation = node_child_process_1.spawnSync;
66
- }
67
- /**
68
- * Returns the tool-home runtime directory used to lazily install the Copilot SDK.
69
- */
70
- function getCopilotRuntimeInstallDir(runtimesDir) {
71
- const override = process.env.CODEX_SWITCH_COPILOT_RUNTIME_DIR;
72
- if (override && override.trim() !== "") {
73
- return path.resolve(override);
74
- }
75
- const baseRuntimesDir = runtimesDir ? path.resolve(runtimesDir) : path.join((0, codex_paths_1.resolveCodexSwitchHome)(), "runtimes");
76
- return path.join(baseRuntimesDir, "copilot");
77
- }
78
- /**
79
- * Returns the package name used by the Copilot runtime installer.
80
- */
81
- function getCopilotSdkPackageName() {
82
- return COPILOT_SDK_PACKAGE;
83
- }
84
- /**
85
- * Returns the supported Copilot SDK package version installed by this release.
86
- */
87
- function getSupportedCopilotSdkVersion() {
88
- return COPILOT_SDK_VERSION;
89
- }
90
- /**
91
- * Returns whether the active Node.js runtime can run the Copilot SDK path.
92
- */
93
- function getCopilotNodeRuntimeStatus(version = process.versions.node) {
94
- const major = Number(version.split(".")[0]);
95
- if (Number.isInteger(major) && major >= COPILOT_MIN_NODE_MAJOR) {
96
- return { ok: true, version };
97
- }
98
- return {
99
- ok: false,
100
- version,
101
- required: `>=${String(COPILOT_MIN_NODE_MAJOR)}`,
102
- };
103
- }
104
- /**
105
- * Fails early when a command path requires the Copilot SDK runtime under Node.js <20.
106
- */
107
- function assertCopilotNodeRuntimeSupported(version = process.versions.node) {
108
- const status = getCopilotNodeRuntimeStatus(version);
109
- if (!status.ok) {
110
- throw (0, errors_1.cliError)("COPILOT_RUNTIME_NODE_UNSUPPORTED", "Copilot runtime support requires Node.js >=20. Direct providers continue to support Node.js >=18.", {
111
- nodeVersion: status.version,
112
- requiredNode: status.required,
113
- });
114
- }
115
- }
116
- /**
117
- * Returns whether an installed Copilot SDK version is supported by this release.
118
- */
119
- function isSupportedCopilotSdkVersion(version) {
120
- if (!version || version.includes("-")) {
121
- return false;
122
- }
123
- return compareSemver(version, COPILOT_SDK_VERSION) >= 0;
124
- }
125
- /**
126
- * Reports whether the optional Copilot SDK runtime is currently installed.
127
- */
128
- function probeCopilotSdkInstall(runtimesDir) {
129
- const installDir = getCopilotRuntimeInstallDir(runtimesDir);
130
- const packageJsonPath = path.join(installDir, "node_modules", "@github", "copilot-sdk", "package.json");
131
- if (!fs.existsSync(packageJsonPath)) {
132
- return {
133
- installed: false,
134
- installDir,
135
- packageName: COPILOT_SDK_PACKAGE,
136
- packageVersion: null,
137
- };
138
- }
139
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
140
- return {
141
- installed: true,
142
- installDir,
143
- packageName: COPILOT_SDK_PACKAGE,
144
- packageVersion: packageJson.version ?? null,
145
- };
146
- }
147
- /**
148
- * Installs the optional Copilot SDK into the user-level runtime directory.
149
- */
150
- function installCopilotSdk(runtimesDir) {
151
- const installDir = getCopilotRuntimeInstallDir(runtimesDir);
152
- fs.mkdirSync(installDir, { recursive: true });
153
- const packageJsonPath = path.join(installDir, "package.json");
154
- if (!fs.existsSync(packageJsonPath)) {
155
- fs.writeFileSync(packageJsonPath, `${JSON.stringify({ name: "codex-switch-copilot-runtime", private: true, version: "0.0.0" }, null, 2)}\n`, "utf8");
156
- }
157
- const installCommand = resolveNpmInstallCommand();
158
- const result = spawnImplementation(installCommand.command, installCommand.args, {
159
- cwd: installDir,
160
- stdio: "pipe",
161
- encoding: "utf8",
162
- shell: false,
163
- });
164
- if (result.error) {
165
- throw (0, errors_1.cliError)("COPILOT_SDK_INSTALL_FAILED", "Failed to install the optional Copilot SDK runtime.", {
166
- installDir,
167
- packageName: COPILOT_SDK_PACKAGE,
168
- cause: result.error.message,
169
- errorCode: result.error.code ?? null,
170
- command: installCommand.command,
171
- args: installCommand.args,
172
- });
173
- }
174
- if (result.status !== 0) {
175
- throw (0, errors_1.cliError)("COPILOT_SDK_INSTALL_FAILED", "Failed to install the optional Copilot SDK runtime.", {
176
- installDir,
177
- packageName: COPILOT_SDK_PACKAGE,
178
- cause: result.stderr || result.stdout || `npm exited with status ${String(result.status)}`,
179
- command: installCommand.command,
180
- args: installCommand.args,
181
- });
182
- }
183
- return probeCopilotSdkInstall(runtimesDir);
184
- }
185
- /**
186
- * Resolves a stable npm install invocation for the optional Copilot SDK runtime.
187
- */
188
- function resolveNpmInstallCommand() {
189
- const installArgs = ["install", "--no-save", `${COPILOT_SDK_PACKAGE}@${COPILOT_SDK_VERSION}`];
190
- const npmCliPath = resolveNpmCliPath();
191
- if (npmCliPath) {
192
- return {
193
- command: process.execPath,
194
- args: [npmCliPath, ...installArgs],
195
- };
196
- }
197
- return {
198
- command: process.platform === "win32" ? "npm.cmd" : "npm",
199
- args: installArgs,
200
- };
201
- }
202
- function compareSemver(left, right) {
203
- const leftParts = left.split(".").map((part) => Number(part));
204
- const rightParts = right.split(".").map((part) => Number(part));
205
- for (let index = 0; index < 3; index += 1) {
206
- const leftPart = Number.isFinite(leftParts[index]) ? leftParts[index] : 0;
207
- const rightPart = Number.isFinite(rightParts[index]) ? rightParts[index] : 0;
208
- if (leftPart !== rightPart) {
209
- return leftPart > rightPart ? 1 : -1;
210
- }
211
- }
212
- return 0;
213
- }
214
- /**
215
- * Finds a locally available npm CLI script near the active Node runtime.
216
- */
217
- function resolveNpmCliPath() {
218
- const execDir = path.dirname(process.execPath);
219
- const candidates = [
220
- process.env.npm_execpath,
221
- path.join(execDir, "node_modules", "npm", "bin", "npm-cli.js"),
222
- path.join(execDir, "..", "node_modules", "npm", "bin", "npm-cli.js"),
223
- path.join(execDir, "..", "..", "node_modules", "npm", "bin", "npm-cli.js"),
224
- ];
225
- for (const candidate of candidates) {
226
- if (candidate && fs.existsSync(candidate)) {
227
- return path.resolve(candidate);
228
- }
229
- }
230
- return null;
231
- }
@@ -1,62 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.getCopilotSdkEntrypoint = getCopilotSdkEntrypoint;
37
- exports.loadCopilotSdk = loadCopilotSdk;
38
- const path = __importStar(require("node:path"));
39
- const node_module_1 = require("node:module");
40
- const errors_1 = require("../domain/errors");
41
- const copilot_installer_1 = require("./copilot-installer");
42
- /**
43
- * Dynamically resolves the lazily installed Copilot SDK entrypoint.
44
- */
45
- function getCopilotSdkEntrypoint(runtimesDir) {
46
- return path.join((0, copilot_installer_1.getCopilotRuntimeInstallDir)(runtimesDir), "node_modules", "@github", "copilot-sdk");
47
- }
48
- /**
49
- * Loads the Copilot SDK only when a Copilot runtime path is exercised.
50
- */
51
- async function loadCopilotSdk(runtimesDir) {
52
- const status = (0, copilot_installer_1.probeCopilotSdkInstall)(runtimesDir);
53
- if (!status.installed) {
54
- throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
55
- installDir: status.installDir,
56
- packageName: status.packageName,
57
- });
58
- }
59
- const runtimePackageJson = path.join(status.installDir, "package.json");
60
- const runtimeRequire = (0, node_module_1.createRequire)(runtimePackageJson);
61
- return runtimeRequire("@github/copilot-sdk");
62
- }