@minniexcode/codex-switch 0.0.7 → 0.0.9

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.
@@ -2,14 +2,18 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.switchProvider = switchProvider;
4
4
  const errors_1 = require("../domain/errors");
5
+ const providers_1 = require("../domain/providers");
5
6
  const config_repo_1 = require("../storage/config-repo");
6
7
  const providers_repo_1 = require("../storage/providers-repo");
7
8
  const auth_repo_1 = require("../storage/auth-repo");
9
+ const copilot_bridge_1 = require("../runtime/copilot-bridge");
10
+ const copilot_installer_1 = require("../runtime/copilot-installer");
11
+ const copilot_adapter_1 = require("../runtime/copilot-adapter");
8
12
  const run_mutation_1 = require("./run-mutation");
9
13
  /**
10
14
  * Switches the active Codex profile and rewrites auth.json for the target provider.
11
15
  */
12
- function switchProvider(args) {
16
+ async function switchProvider(args) {
13
17
  const providers = (0, providers_repo_1.readProvidersFile)(args.providersPath);
14
18
  const provider = providers.providers[args.providerName];
15
19
  if (!provider) {
@@ -27,6 +31,76 @@ function switchProvider(args) {
27
31
  runtimeEnvKey: envKey,
28
32
  });
29
33
  }
34
+ if ((0, providers_1.isCopilotBridgeProvider)(provider)) {
35
+ const installStatus = (0, copilot_installer_1.probeCopilotSdkInstall)();
36
+ if (!installStatus.installed) {
37
+ throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", {
38
+ installDir: installStatus.installDir,
39
+ packageName: installStatus.packageName,
40
+ });
41
+ }
42
+ await (0, copilot_adapter_1.readCopilotAuthState)();
43
+ const bridge = await (0, copilot_bridge_1.ensureCopilotBridge)(args.providerName, provider);
44
+ const nextProvider = bridge.portChanged
45
+ ? (0, providers_1.cleanProviderRecord)({
46
+ ...provider,
47
+ baseUrl: bridge.baseUrl,
48
+ runtime: {
49
+ ...provider.runtime,
50
+ bridgePort: bridge.port,
51
+ },
52
+ })
53
+ : provider;
54
+ try {
55
+ return (0, run_mutation_1.runMutation)({
56
+ codexDir: args.codexDir,
57
+ backupsDir: args.backupsDir,
58
+ latestBackupPath: args.latestBackupPath,
59
+ operation: "switch",
60
+ files: [
61
+ { absolutePath: args.configPath, relativePath: "config.toml" },
62
+ { absolutePath: args.authPath, relativePath: "auth.json" },
63
+ ],
64
+ mutate: () => {
65
+ const configPlan = (0, config_repo_1.createConfigMutationPlan)(document, {
66
+ setActiveProfile: provider.profile,
67
+ upsertModelProviders: bridge.portChanged
68
+ ? {
69
+ [provider.profile]: {
70
+ baseUrl: (0, providers_1.buildCopilotBridgeBaseUrl)(nextProvider.runtime),
71
+ envKey,
72
+ },
73
+ }
74
+ : undefined,
75
+ });
76
+ if (bridge.portChanged) {
77
+ (0, providers_repo_1.writeProvidersFile)(args.providersPath, {
78
+ providers: {
79
+ ...providers.providers,
80
+ [args.providerName]: nextProvider,
81
+ },
82
+ });
83
+ }
84
+ (0, config_repo_1.applyConfigMutation)(args.configPath, document, configPlan);
85
+ const existingAuth = (0, auth_repo_1.readAuthFileIfExists)(args.authPath);
86
+ (0, auth_repo_1.writeAuthFile)(args.authPath, nextProvider, existingAuth ?? undefined);
87
+ return {
88
+ provider: args.providerName,
89
+ profile: nextProvider.profile,
90
+ envKey: nextProvider.envKey,
91
+ portChanged: bridge.portChanged,
92
+ bridgePort: bridge.port,
93
+ };
94
+ },
95
+ });
96
+ }
97
+ catch (error) {
98
+ if (!bridge.reused) {
99
+ (0, copilot_bridge_1.stopCopilotBridge)();
100
+ }
101
+ throw error;
102
+ }
103
+ }
30
104
  return (0, run_mutation_1.runMutation)({
31
105
  codexDir: args.codexDir,
32
106
  backupsDir: args.backupsDir,
package/dist/cli.js CHANGED
@@ -9,7 +9,7 @@ const args_1 = require("./commands/args");
9
9
  const help_1 = require("./commands/help");
10
10
  const errors_1 = require("./domain/errors");
11
11
  const output_1 = require("./cli/output");
12
- const VERSION = "0.0.7";
12
+ const VERSION = "0.0.8";
13
13
  /**
14
14
  * Prints the command help text to stdout.
15
15
  */
@@ -48,6 +48,7 @@ const list_providers_1 = require("../app/list-providers");
48
48
  const remove_provider_1 = require("../app/remove-provider");
49
49
  const rollback_backup_1 = require("../app/rollback-backup");
50
50
  const run_doctor_1 = require("../app/run-doctor");
51
+ const bridge_1 = require("../app/bridge");
51
52
  const setup_codex_1 = require("../app/setup-codex");
52
53
  const show_config_1 = require("../app/show-config");
53
54
  const show_provider_1 = require("../app/show-provider");
@@ -58,6 +59,7 @@ const providers_1 = require("../domain/providers");
58
59
  const add_interactive_1 = require("../interaction/add-interactive");
59
60
  const interactive_1 = require("../interaction/interactive");
60
61
  const prompt_1 = require("../interaction/prompt");
62
+ const copilot_installer_1 = require("../runtime/copilot-installer");
61
63
  const config_repo_1 = require("../storage/config-repo");
62
64
  const codex_paths_1 = require("../storage/codex-paths");
63
65
  const providers_repo_1 = require("../storage/providers-repo");
@@ -89,6 +91,36 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
89
91
  return (0, get_current_profile_1.getCurrentProfile)(paths.configPath);
90
92
  case "status":
91
93
  return (0, get_status_1.getStatus)(paths.codexDir, paths.configPath, paths.providersPath, paths.authPath);
94
+ case "bridge-start": {
95
+ const providerName = parsed.positionals[0] ?? null;
96
+ return (0, bridge_1.startBridge)({
97
+ providersPath: paths.providersPath,
98
+ configPath: paths.configPath,
99
+ providerName,
100
+ runtime,
101
+ json: ctx.options.json,
102
+ });
103
+ }
104
+ case "bridge-stop": {
105
+ const providerName = parsed.positionals[0] ?? null;
106
+ return (0, bridge_1.stopBridge)({
107
+ providersPath: paths.providersPath,
108
+ configPath: paths.configPath,
109
+ providerName,
110
+ runtime,
111
+ json: ctx.options.json,
112
+ });
113
+ }
114
+ case "bridge-status": {
115
+ const providerName = parsed.positionals[0] ?? null;
116
+ return (0, bridge_1.statusBridge)({
117
+ providersPath: paths.providersPath,
118
+ configPath: paths.configPath,
119
+ providerName,
120
+ runtime,
121
+ json: ctx.options.json,
122
+ });
123
+ }
92
124
  case "init": {
93
125
  let codexDir = ctx.options.codexDir;
94
126
  const candidates = (0, config_repo_1.findCodexDirCandidates)(ctx.options.codexDirExplicit ? ctx.options.codexDir : null);
@@ -155,6 +187,9 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
155
187
  if (!providerName) {
156
188
  throw (0, errors_1.cliError)("PROVIDER_NOT_FOUND", "Missing provider name for switch command.");
157
189
  }
190
+ if ((0, args_1.hasFlag)(parsed.commandOptions, "--install-copilot-sdk")) {
191
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--install-copilot-sdk is only supported with add --copilot.");
192
+ }
158
193
  return (0, switch_provider_1.switchProvider)({
159
194
  codexDir: paths.codexDir,
160
195
  backupsDir: paths.backupsDir,
@@ -221,7 +256,22 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
221
256
  let note = (0, args_1.getSingleOption)(parsed.commandOptions, "--note", false);
222
257
  let tags = parsed.commandOptions.get("--tag") ?? [];
223
258
  let createProfile = (0, args_1.hasFlag)(parsed.commandOptions, "--create-profile");
224
- if (!providerName || !profile || !apiKey) {
259
+ const copilot = (0, args_1.hasFlag)(parsed.commandOptions, "--copilot");
260
+ const bridgeHost = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-host", false);
261
+ const bridgePortValue = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-port", false);
262
+ const bridgeApiKey = (0, args_1.getSingleOption)(parsed.commandOptions, "--bridge-api-key", false);
263
+ let installCopilotSdk = (0, args_1.hasFlag)(parsed.commandOptions, "--install-copilot-sdk");
264
+ const bridgePort = bridgePortValue ? Number(bridgePortValue) : null;
265
+ if (copilot && apiKey) {
266
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--copilot does not allow --api-key. Use --bridge-api-key for the local bridge secret.");
267
+ }
268
+ if (bridgePortValue && (!Number.isInteger(bridgePort) || bridgePort === null || bridgePort <= 0)) {
269
+ throw (0, errors_1.cliError)("INVALID_ARGUMENT", "--bridge-port must be a positive integer.");
270
+ }
271
+ if (copilot && !installCopilotSdk && (0, interactive_1.canPrompt)(runtime, ctx.options.json) && !(0, copilot_installer_1.probeCopilotSdkInstall)().installed) {
272
+ installCopilotSdk = await runtime.confirmAction("The optional Copilot SDK runtime is not installed. Install it now?");
273
+ }
274
+ if (!providerName || !profile || (!apiKey && !copilot)) {
225
275
  if (ctx.options.json || !runtime.isInteractive()) {
226
276
  throw (0, add_interactive_1.createNonInteractiveAddError)();
227
277
  }
@@ -251,12 +301,18 @@ async function handleRegisteredCommand(ctx, parsed, runtime = (0, prompt_1.creat
251
301
  authPath: paths.authPath,
252
302
  providerName,
253
303
  profile,
254
- apiKey,
304
+ apiKey: apiKey ?? "",
255
305
  baseUrl,
256
306
  model,
257
307
  note,
258
308
  tags,
259
309
  createProfile,
310
+ copilot,
311
+ bridgeHost,
312
+ bridgePort,
313
+ bridgeApiKey,
314
+ installCopilotSdk,
315
+ interactive: (0, interactive_1.canPrompt)(runtime, ctx.options.json),
260
316
  });
261
317
  }
262
318
  case "edit": {
@@ -63,6 +63,7 @@ function buildHelpText(commandName) {
63
63
  " codexs migrate",
64
64
  " codexs list",
65
65
  " codexs switch",
66
+ " codexs bridge start",
66
67
  " codexs add packycode --profile packycode --api-key sk-xxx",
67
68
  " codexs config show",
68
69
  " codexs remove freemodel",
@@ -41,6 +41,48 @@ exports.COMMANDS = [
41
41
  ],
42
42
  examples: ["codexs config list-profiles", "codexs config list-profiles --json"],
43
43
  },
44
+ {
45
+ id: "bridge-start",
46
+ tokens: ["bridge", "start"],
47
+ handler: handlers_1.handleRegisteredCommand,
48
+ group: "write",
49
+ summary: "Start or reuse the managed Copilot bridge.",
50
+ usage: ["codexs bridge start [provider] [--json] [--codex-dir <path>]"],
51
+ details: [
52
+ "Resolves a Copilot bridge provider by explicit name, active provider, sole provider, or TTY selection.",
53
+ "Reuses a healthy bridge for the same provider and replaces a different managed provider when needed.",
54
+ "If the preferred port is occupied, automatically selects another free 5-digit port and persists it.",
55
+ ],
56
+ examples: ["codexs bridge start", "codexs bridge start copilot-main"],
57
+ },
58
+ {
59
+ id: "bridge-stop",
60
+ tokens: ["bridge", "stop"],
61
+ handler: handlers_1.handleRegisteredCommand,
62
+ group: "recovery",
63
+ summary: "Stop the managed Copilot bridge.",
64
+ usage: ["codexs bridge stop [provider] [--json] [--codex-dir <path>]"],
65
+ details: [
66
+ "Prefers the runtime-state instance when present and uses an explicit provider as a guard.",
67
+ "Clears the runtime-state manifest without mutating providers.json or auth.json.",
68
+ "Is idempotent when no managed bridge is currently running.",
69
+ ],
70
+ examples: ["codexs bridge stop", "codexs bridge stop copilot-main"],
71
+ },
72
+ {
73
+ id: "bridge-status",
74
+ tokens: ["bridge", "status"],
75
+ handler: handlers_1.handleRegisteredCommand,
76
+ group: "read",
77
+ summary: "Inspect the managed Copilot bridge.",
78
+ usage: ["codexs bridge status [provider] [--json] [--codex-dir <path>]"],
79
+ details: [
80
+ "Reports runtime-state, provider binding, and whether the live worker matches the expected provider.",
81
+ "Prefers the runtime-state instance when one is present.",
82
+ "Uses an explicit provider as a guard instead of silently switching targets.",
83
+ ],
84
+ examples: ["codexs bridge status", "codexs bridge status copilot-main"],
85
+ },
44
86
  {
45
87
  id: "init",
46
88
  tokens: ["init"],
@@ -128,6 +170,7 @@ exports.COMMANDS = [
128
170
  usage: ["codexs status [--json] [--codex-dir <path>]"],
129
171
  details: [
130
172
  "Reports file presence, current profile, and whether the live profile is mapped.",
173
+ "When the active provider uses a local runtime bridge, status also reports bridge and SDK state.",
131
174
  "Surfaces config consistency signals without mutating any files.",
132
175
  "Use doctor for deeper diagnostics.",
133
176
  ],
@@ -160,6 +203,7 @@ exports.COMMANDS = [
160
203
  summary: "Add a provider with explicit flags or progressive TTY prompts.",
161
204
  usage: [
162
205
  "codexs add <provider> --profile <name> --api-key <key> [--base-url <url>] [--note <text>] [--tag <tag> ...]",
206
+ "codexs add <provider> --copilot --profile <name> [--bridge-host <host>] [--bridge-port <port>] [--bridge-api-key <secret>] [--install-copilot-sdk]",
163
207
  "codexs add <provider> --profile <name> --api-key <key> --create-profile --model <name> --base-url <url>",
164
208
  "codexs add [--profile <name>] [--api-key <key>] [--base-url <url>] [--note <text>] [--tag <tag> ...]",
165
209
  ],
@@ -170,8 +214,13 @@ exports.COMMANDS = [
170
214
  "Interactive tags use preset multi-select only.",
171
215
  "Automation and non-TTY environments must pass all required values explicitly.",
172
216
  "Creating a missing profile section requires --create-profile together with --model and --base-url.",
217
+ "Use --copilot to create a GitHub Copilot bridge provider backed by the official SDK.",
218
+ ],
219
+ examples: [
220
+ "codexs add packycode --profile packycode --api-key sk-xxx",
221
+ "codexs add copilot-main --copilot --profile copilot-main --install-copilot-sdk",
222
+ "codexs add",
173
223
  ],
174
- examples: ["codexs add packycode --profile packycode --api-key sk-xxx", "codexs add packycode --profile packycode", "codexs add"],
175
224
  },
176
225
  {
177
226
  id: "switch",
@@ -184,6 +233,7 @@ exports.COMMANDS = [
184
233
  "When <provider> is omitted in a TTY, an interactive provider selector is shown.",
185
234
  "When <provider> is passed explicitly, switch proceeds directly without extra confirmation.",
186
235
  "Switch updates the active config profile and rewrites auth.json from the provider envKey/apiKey pair.",
236
+ "Copilot bridge providers probe the optional official SDK before switching and fail fast if it is missing.",
187
237
  "Backs up config.toml and auth.json, then rolls back on failure.",
188
238
  ],
189
239
  examples: ["codexs switch freemodel", "codexs switch packycode --json"],
@@ -252,7 +302,11 @@ exports.COMMANDS = [
252
302
  group: "recovery",
253
303
  summary: "Run configuration and environment diagnostics.",
254
304
  usage: ["codexs doctor [--json] [--codex-dir <path>]"],
255
- details: ["Checks the expected config files, provider/profile consistency, and Codex CLI availability.", "Returns structured issues so users and AI agents can act on them."],
305
+ details: [
306
+ "Checks the expected config files, provider/profile consistency, and Codex CLI availability.",
307
+ "Copilot bridge providers add runtime dependency, auth, and bridge health diagnostics.",
308
+ "Returns structured issues so users and AI agents can act on them.",
309
+ ],
256
310
  examples: ["codexs doctor", "codexs doctor --json"],
257
311
  },
258
312
  {
@@ -6,6 +6,9 @@ exports.sortProviders = sortProviders;
6
6
  exports.findProviderByProfile = findProviderByProfile;
7
7
  exports.findProvidersByProfile = findProvidersByProfile;
8
8
  exports.maskSecret = maskSecret;
9
+ exports.isRuntimeBackedProvider = isRuntimeBackedProvider;
10
+ exports.isCopilotBridgeProvider = isCopilotBridgeProvider;
11
+ exports.buildCopilotBridgeBaseUrl = buildCopilotBridgeBaseUrl;
9
12
  /**
10
13
  * Validates and normalizes unknown JSON into the providers.json domain model.
11
14
  */
@@ -42,6 +45,13 @@ function validateProvidersShape(input) {
42
45
  (!Array.isArray(provider.tags) || provider.tags.some((tag) => typeof tag !== "string"))) {
43
46
  throw new Error(`Provider "${name}" has invalid tags.`);
44
47
  }
48
+ if (provider.runtime !== undefined) {
49
+ validateProviderRuntime(name, provider.runtime);
50
+ const expectedBaseUrl = buildCopilotBridgeBaseUrl(provider.runtime);
51
+ if (typeof provider.baseUrl !== "string" || provider.baseUrl.trim() !== expectedBaseUrl) {
52
+ throw new Error(`Provider "${name}" baseUrl must match runtime bridge base URL "${expectedBaseUrl}".`);
53
+ }
54
+ }
45
55
  // Normalize provider fields during validation so the persisted format stays clean.
46
56
  providers[name] = cleanProviderRecord({
47
57
  profile: provider.profile,
@@ -50,6 +60,7 @@ function validateProvidersShape(input) {
50
60
  baseUrl: provider.baseUrl,
51
61
  note: provider.note,
52
62
  tags: provider.tags,
63
+ runtime: provider.runtime,
53
64
  });
54
65
  }
55
66
  return { providers };
@@ -72,6 +83,18 @@ function cleanProviderRecord(record) {
72
83
  if (record.tags && record.tags.length > 0) {
73
84
  next.tags = record.tags.map((tag) => tag.trim()).filter((tag) => tag.length > 0);
74
85
  }
86
+ if (record.runtime) {
87
+ next.runtime = {
88
+ kind: record.runtime.kind,
89
+ upstream: record.runtime.upstream,
90
+ bridgeHost: record.runtime.bridgeHost.trim(),
91
+ bridgePort: record.runtime.bridgePort,
92
+ bridgePath: record.runtime.bridgePath,
93
+ premiumRequests: record.runtime.premiumRequests,
94
+ authSource: record.runtime.authSource,
95
+ sdkInstallMode: record.runtime.sdkInstallMode,
96
+ };
97
+ }
75
98
  return next;
76
99
  }
77
100
  /**
@@ -114,3 +137,54 @@ function maskSecret(value) {
114
137
  }
115
138
  return `${value.slice(0, 3)}***${value.slice(-2)}`;
116
139
  }
140
+ /**
141
+ * Returns whether one provider record relies on an auxiliary runtime component.
142
+ */
143
+ function isRuntimeBackedProvider(provider) {
144
+ return Boolean(provider.runtime);
145
+ }
146
+ /**
147
+ * Returns whether one provider uses the GitHub Copilot SDK bridge runtime.
148
+ */
149
+ function isCopilotBridgeProvider(provider) {
150
+ return provider.runtime?.kind === "copilot-sdk-bridge";
151
+ }
152
+ /**
153
+ * Builds the canonical local bridge URL for one Copilot runtime provider.
154
+ */
155
+ function buildCopilotBridgeBaseUrl(runtime) {
156
+ return `http://${runtime.bridgeHost}:${runtime.bridgePort}${runtime.bridgePath}`;
157
+ }
158
+ /**
159
+ * Validates one runtime-backed provider block.
160
+ */
161
+ function validateProviderRuntime(name, runtime) {
162
+ if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
163
+ throw new Error(`Provider "${name}" has an invalid runtime block.`);
164
+ }
165
+ const record = runtime;
166
+ if (record.kind !== "copilot-sdk-bridge") {
167
+ throw new Error(`Provider "${name}" has an unsupported runtime kind.`);
168
+ }
169
+ if (record.upstream !== "github-copilot") {
170
+ throw new Error(`Provider "${name}" has an invalid runtime upstream.`);
171
+ }
172
+ if (typeof record.bridgeHost !== "string" || record.bridgeHost.trim() === "") {
173
+ throw new Error(`Provider "${name}" has an invalid runtime bridgeHost.`);
174
+ }
175
+ if (typeof record.bridgePort !== "number" || !Number.isInteger(record.bridgePort) || record.bridgePort <= 0) {
176
+ throw new Error(`Provider "${name}" has an invalid runtime bridgePort.`);
177
+ }
178
+ if (record.bridgePath !== "/v1") {
179
+ throw new Error(`Provider "${name}" has an invalid runtime bridgePath.`);
180
+ }
181
+ if (record.premiumRequests !== true) {
182
+ throw new Error(`Provider "${name}" must enable runtime premiumRequests.`);
183
+ }
184
+ if (record.authSource !== "official-sdk") {
185
+ throw new Error(`Provider "${name}" has an invalid runtime authSource.`);
186
+ }
187
+ if (record.sdkInstallMode !== "lazy") {
188
+ throw new Error(`Provider "${name}" has an invalid runtime sdkInstallMode.`);
189
+ }
190
+ }
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.probeCopilotSdkRuntime = probeCopilotSdkRuntime;
4
+ exports.requireCopilotSdk = requireCopilotSdk;
5
+ exports.readCopilotAuthState = readCopilotAuthState;
6
+ exports.sendCopilotChatCompletion = sendCopilotChatCompletion;
7
+ const errors_1 = require("../domain/errors");
8
+ const copilot_sdk_loader_1 = require("./copilot-sdk-loader");
9
+ const copilot_installer_1 = require("./copilot-installer");
10
+ /**
11
+ * Probes whether the optional Copilot SDK runtime is installed and loadable.
12
+ */
13
+ function probeCopilotSdkRuntime() {
14
+ const status = (0, copilot_installer_1.probeCopilotSdkInstall)();
15
+ if (!status.installed) {
16
+ return {
17
+ ok: false,
18
+ runtime: "copilot-sdk",
19
+ reason: "missing",
20
+ cause: "The optional Copilot SDK runtime is not installed.",
21
+ details: {
22
+ installDir: status.installDir,
23
+ packageName: status.packageName,
24
+ },
25
+ };
26
+ }
27
+ return {
28
+ ok: true,
29
+ runtime: "copilot-sdk",
30
+ version: status.packageVersion ?? undefined,
31
+ details: {
32
+ installDir: status.installDir,
33
+ packageName: status.packageName,
34
+ },
35
+ };
36
+ }
37
+ /**
38
+ * Loads the lazily installed Copilot SDK and returns the module.
39
+ */
40
+ async function requireCopilotSdk() {
41
+ return (0, copilot_sdk_loader_1.loadCopilotSdk)();
42
+ }
43
+ /**
44
+ * Probes whether the lazily installed Copilot SDK can create a usable session.
45
+ */
46
+ async function readCopilotAuthState() {
47
+ const runtime = probeCopilotSdkRuntime();
48
+ if (!runtime.ok) {
49
+ throw (0, errors_1.cliError)("COPILOT_SDK_MISSING", "The optional Copilot SDK runtime is not installed.", runtime.details);
50
+ }
51
+ const { client, session } = await createCopilotSession();
52
+ await stopCopilotClient(client);
53
+ return {
54
+ ready: Boolean(session),
55
+ source: "official-sdk",
56
+ mode: "session",
57
+ };
58
+ }
59
+ /**
60
+ * Executes a single chat-completions style request through the optional Copilot SDK when available.
61
+ */
62
+ async function sendCopilotChatCompletion(args) {
63
+ const { client, session, sdk } = await createCopilotSession();
64
+ try {
65
+ const sendAndWait = resolveCallable(session, "sendAndWait") ?? resolveCallable(sdk, "sendAndWait");
66
+ if (!sendAndWait) {
67
+ throw (0, errors_1.cliError)("COPILOT_SDK_UNSUPPORTED", "The installed Copilot SDK does not expose a supported sendAndWait API.", {
68
+ provider: args.provider,
69
+ });
70
+ }
71
+ const prompt = Array.isArray(args.payload.messages)
72
+ ? args.payload.messages
73
+ .map((entry) => {
74
+ const message = entry;
75
+ return `${String(message.role ?? "user")}: ${String(message.content ?? "")}`;
76
+ })
77
+ .join("\n")
78
+ : "";
79
+ const result = await Promise.resolve(sendAndWait({ model: args.payload.model, prompt }));
80
+ const content = typeof result === "string"
81
+ ? result
82
+ : typeof result?.content === "string"
83
+ ? String(result.content)
84
+ : typeof result?.data === "object" &&
85
+ typeof result.data.content === "string"
86
+ ? String(result.data.content)
87
+ : JSON.stringify(result);
88
+ return {
89
+ id: `copilot-${Date.now()}`,
90
+ object: "chat.completion",
91
+ created: Math.floor(Date.now() / 1000),
92
+ model: args.payload.model ?? "copilot",
93
+ choices: [
94
+ {
95
+ index: 0,
96
+ message: {
97
+ role: "assistant",
98
+ content,
99
+ },
100
+ finish_reason: "stop",
101
+ },
102
+ ],
103
+ };
104
+ }
105
+ finally {
106
+ await stopCopilotClient(client);
107
+ }
108
+ }
109
+ async function createCopilotSession() {
110
+ const sdk = (await requireCopilotSdk());
111
+ const client = createCopilotClient(sdk);
112
+ const createSession = resolveCallable(client ? client : null, "createSession") ?? resolveCallable(sdk, "createSession");
113
+ if (!createSession) {
114
+ throw (0, errors_1.cliError)("COPILOT_SDK_UNSUPPORTED", "The installed Copilot SDK does not expose a supported createSession API.", {});
115
+ }
116
+ try {
117
+ const session = (await Promise.resolve(createSession({})));
118
+ return {
119
+ sdk,
120
+ client,
121
+ session,
122
+ };
123
+ }
124
+ catch (error) {
125
+ throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "Copilot authentication is required before the local bridge can be used.", {
126
+ cause: error instanceof Error ? error.message : String(error),
127
+ });
128
+ }
129
+ }
130
+ function createCopilotClient(sdk) {
131
+ const ClientCtor = resolveConstructor(sdk, "CopilotClient");
132
+ if (!ClientCtor) {
133
+ return null;
134
+ }
135
+ try {
136
+ return new ClientCtor();
137
+ }
138
+ catch (error) {
139
+ throw (0, errors_1.cliError)("COPILOT_SDK_UNSUPPORTED", "The installed Copilot SDK CopilotClient could not be constructed.", {
140
+ cause: error instanceof Error ? error.message : String(error),
141
+ });
142
+ }
143
+ }
144
+ async function stopCopilotClient(client) {
145
+ if (client && typeof client.stop === "function") {
146
+ await Promise.resolve(client.stop());
147
+ }
148
+ }
149
+ function resolveCallable(target, name) {
150
+ if (!target) {
151
+ return null;
152
+ }
153
+ const direct = target[name];
154
+ if (typeof direct === "function") {
155
+ return direct;
156
+ }
157
+ const nestedDefault = target.default;
158
+ if (nestedDefault && typeof nestedDefault[name] === "function") {
159
+ return nestedDefault[name];
160
+ }
161
+ return null;
162
+ }
163
+ function resolveConstructor(target, name) {
164
+ const direct = target[name];
165
+ if (typeof direct === "function") {
166
+ return direct;
167
+ }
168
+ const nestedDefault = target.default;
169
+ if (nestedDefault && typeof nestedDefault[name] === "function") {
170
+ return nestedDefault[name];
171
+ }
172
+ return null;
173
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const copilot_bridge_1 = require("./copilot-bridge");
4
+ const copilot_adapter_1 = require("./copilot-adapter");
5
+ async function main() {
6
+ const provider = process.env.CODEX_SWITCH_BRIDGE_PROVIDER ?? "copilot";
7
+ const host = process.env.CODEX_SWITCH_BRIDGE_HOST ?? "127.0.0.1";
8
+ const port = Number(process.env.CODEX_SWITCH_BRIDGE_PORT ?? "41415");
9
+ const apiKey = process.env.CODEX_SWITCH_BRIDGE_API_KEY ?? "";
10
+ await (0, copilot_bridge_1.startCopilotBridgeServer)({
11
+ host,
12
+ port,
13
+ apiKey,
14
+ executeChatCompletion: async (payload) => (0, copilot_adapter_1.sendCopilotChatCompletion)({
15
+ provider,
16
+ payload,
17
+ }),
18
+ });
19
+ }
20
+ if (require.main === module) {
21
+ void main().catch((error) => {
22
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
23
+ process.exit(1);
24
+ });
25
+ }