@minniexcode/codex-switch 0.1.1 → 0.1.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/README.CN.md +6 -4
- package/README.md +15 -4
- package/dist/app/add-provider.js +1 -0
- package/dist/app/bridge.js +2 -1
- package/dist/app/switch-provider.js +2 -1
- package/dist/commands/handlers.js +1 -0
- package/dist/domain/config.js +45 -1
- package/dist/domain/providers.js +1 -0
- package/dist/runtime/copilot-adapter.js +346 -70
- package/dist/runtime/copilot-bridge-worker.js +27 -2
- package/dist/runtime/copilot-bridge.js +192 -10
- package/dist/runtime/copilot-cli.js +22 -0
- package/dist/runtime/copilot-installer.js +59 -1
- package/dist/runtime/copilot-sdk-loader.js +4 -1
- package/docs/Design/codex-switch-v0.1.0-design.md +32 -152
- package/docs/Design/codex-switch-v0.1.1-design.md +15 -26
- package/docs/Design/codex-switch-v0.1.2-design.md +65 -0
- package/docs/Design/codex-switch-v0.1.3-design.md +10 -0
- package/docs/PRD/codex-switch-prd-v0.1.0.md +65 -217
- package/docs/PRD/codex-switch-prd-v0.1.1.md +26 -0
- package/docs/PRD/codex-switch-prd-v0.1.2.md +41 -0
- package/docs/PRD/codex-switch-prd-v0.1.3.md +22 -0
- package/docs/Tests/testing.md +1 -1
- package/docs/cli-usage.md +14 -4
- package/docs/codex-switch-command-design.md +1 -1
- package/docs/codex-switch-product-overview.md +7 -3
- package/docs/codex-switch-product-research.md +2 -2
- package/docs/codex-switch-technical-architecture.md +86 -1115
- package/package.json +1 -1
- package/docs/Design/codex-switch-copilot-integration-design.md +0 -517
- package/docs/Design/codex-switch-v0.0.10-design.md +0 -669
- package/docs/Design/codex-switch-v0.0.11-design.md +0 -824
- package/docs/Design/codex-switch-v0.0.12-design.md +0 -343
- package/docs/Design/codex-switch-v0.0.4-design.md +0 -874
- package/docs/Design/codex-switch-v0.0.5-design.md +0 -932
- package/docs/Design/codex-switch-v0.0.6-design.md +0 -708
- package/docs/Design/codex-switch-v0.0.7-design.md +0 -862
- package/docs/Design/codex-switch-v0.0.8-design.md +0 -132
- package/docs/Design/codex-switch-v0.0.9-design.md +0 -182
- package/docs/Design/codex-switch-v0.0.9-to-v0.0.12-roadmap.md +0 -413
- package/docs/PRD/codex-switch-prd-v0.0.10.md +0 -406
- package/docs/PRD/codex-switch-prd-v0.0.11.md +0 -577
- package/docs/PRD/codex-switch-prd-v0.0.12.md +0 -279
- package/docs/PRD/codex-switch-prd-v0.0.5-to-v0.1.0.md +0 -446
- package/docs/PRD/codex-switch-prd-v0.0.8.md +0 -62
- package/docs/PRD/codex-switch-prd-v0.0.9.md +0 -166
- package/docs/PRD/codex-switch-prd.md +0 -650
- package/docs/Tests/test-report-0.0.5.md +0 -163
- package/docs/Tests/test-report-0.0.7.md +0 -118
- package/docs/Tests/testing-bridge-v0.0.9.md +0 -367
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventEmitter = void 0;
|
|
3
4
|
exports.probeCopilotSdkRuntime = probeCopilotSdkRuntime;
|
|
4
5
|
exports.requireCopilotSdk = requireCopilotSdk;
|
|
5
6
|
exports.readCopilotAuthState = readCopilotAuthState;
|
|
7
|
+
exports.createCopilotRuntimeClient = createCopilotRuntimeClient;
|
|
8
|
+
exports.startCopilotRuntimeClient = startCopilotRuntimeClient;
|
|
9
|
+
exports.stopCopilotRuntimeClient = stopCopilotRuntimeClient;
|
|
6
10
|
exports.sendCopilotChatCompletion = sendCopilotChatCompletion;
|
|
11
|
+
const node_events_1 = require("node:events");
|
|
12
|
+
Object.defineProperty(exports, "EventEmitter", { enumerable: true, get: function () { return node_events_1.EventEmitter; } });
|
|
7
13
|
const errors_1 = require("../domain/errors");
|
|
8
14
|
const copilot_sdk_loader_1 = require("./copilot-sdk-loader");
|
|
9
15
|
const copilot_installer_1 = require("./copilot-installer");
|
|
16
|
+
const copilot_cli_1 = require("./copilot-cli");
|
|
17
|
+
const DEFAULT_UPSTREAM_TIMEOUT_MS = 300000;
|
|
10
18
|
/**
|
|
11
|
-
* Probes whether the optional Copilot SDK runtime is installed and
|
|
19
|
+
* Probes whether the optional Copilot SDK runtime is installed, supported, and shaped correctly.
|
|
12
20
|
*/
|
|
13
21
|
function probeCopilotSdkRuntime(runtimesDir) {
|
|
22
|
+
const nodeStatus = safeCopilotNodeRuntimeStatus();
|
|
23
|
+
if (!nodeStatus.ok) {
|
|
24
|
+
return nodeStatus.availability;
|
|
25
|
+
}
|
|
14
26
|
const status = (0, copilot_installer_1.probeCopilotSdkInstall)(runtimesDir);
|
|
15
27
|
if (!status.installed) {
|
|
16
28
|
return {
|
|
@@ -24,6 +36,21 @@ function probeCopilotSdkRuntime(runtimesDir) {
|
|
|
24
36
|
},
|
|
25
37
|
};
|
|
26
38
|
}
|
|
39
|
+
if (!(0, copilot_installer_1.isSupportedCopilotSdkVersion)(status.packageVersion)) {
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
runtime: "copilot-sdk",
|
|
43
|
+
reason: "unsupported",
|
|
44
|
+
version: status.packageVersion ?? undefined,
|
|
45
|
+
cause: `The installed Copilot SDK version is unsupported. Install ${status.packageName}@${(0, copilot_installer_1.getSupportedCopilotSdkVersion)()}.`,
|
|
46
|
+
details: {
|
|
47
|
+
installDir: status.installDir,
|
|
48
|
+
packageName: status.packageName,
|
|
49
|
+
packageVersion: status.packageVersion,
|
|
50
|
+
supportedVersion: (0, copilot_installer_1.getSupportedCopilotSdkVersion)(),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
27
54
|
return {
|
|
28
55
|
ok: true,
|
|
29
56
|
runtime: "copilot-sdk",
|
|
@@ -41,55 +68,83 @@ async function requireCopilotSdk(runtimesDir) {
|
|
|
41
68
|
return (0, copilot_sdk_loader_1.loadCopilotSdk)(runtimesDir);
|
|
42
69
|
}
|
|
43
70
|
/**
|
|
44
|
-
* Probes
|
|
71
|
+
* Probes Copilot auth readiness through the SDK client status endpoint.
|
|
45
72
|
*/
|
|
46
73
|
async function readCopilotAuthState(runtimesDir) {
|
|
47
74
|
const runtime = probeCopilotSdkRuntime(runtimesDir);
|
|
48
75
|
if (!runtime.ok) {
|
|
49
|
-
throw (0, errors_1.cliError)("
|
|
76
|
+
throw (0, errors_1.cliError)(runtime.reason === "unsupported" ? "COPILOT_SDK_VERSION_UNSUPPORTED" : "COPILOT_SDK_MISSING", runtime.cause, runtime.details);
|
|
50
77
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
const runtimeClient = await createCopilotRuntimeClient(runtimesDir);
|
|
79
|
+
try {
|
|
80
|
+
await startCopilotClient(runtimeClient.client);
|
|
81
|
+
const getAuthStatus = resolveCallable(runtimeClient.client, "getAuthStatus");
|
|
82
|
+
if (!getAuthStatus) {
|
|
83
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose CopilotClient.getAuthStatus().", {});
|
|
84
|
+
}
|
|
85
|
+
const status = await Promise.resolve(getAuthStatus());
|
|
86
|
+
if (!isAuthReady(status)) {
|
|
87
|
+
throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "Copilot authentication is required before the local bridge can be used.", {
|
|
88
|
+
status,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
ready: true,
|
|
93
|
+
source: "official-sdk",
|
|
94
|
+
mode: "auth-status",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
await stopCopilotClient(runtimeClient.client);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Creates one long-lived Copilot SDK client for bridge workers.
|
|
103
|
+
*/
|
|
104
|
+
async function createCopilotRuntimeClient(runtimesDir) {
|
|
105
|
+
(0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
|
|
106
|
+
const runtime = probeCopilotSdkRuntime(runtimesDir);
|
|
107
|
+
if (!runtime.ok) {
|
|
108
|
+
throw (0, errors_1.cliError)(runtime.reason === "unsupported" ? "COPILOT_SDK_VERSION_UNSUPPORTED" : "COPILOT_SDK_MISSING", runtime.cause, runtime.details);
|
|
109
|
+
}
|
|
110
|
+
const sdk = (await requireCopilotSdk(runtimesDir));
|
|
111
|
+
const client = createCopilotClient(sdk, runtimesDir);
|
|
112
|
+
assertCopilotSdkApiContract(sdk, client);
|
|
113
|
+
return { sdk, client };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Starts a Copilot SDK client if the installed SDK exposes an explicit start hook.
|
|
117
|
+
*/
|
|
118
|
+
async function startCopilotRuntimeClient(runtimeClient) {
|
|
119
|
+
await startCopilotClient(runtimeClient.client);
|
|
58
120
|
}
|
|
59
121
|
/**
|
|
60
|
-
*
|
|
122
|
+
* Stops a Copilot SDK client.
|
|
123
|
+
*/
|
|
124
|
+
async function stopCopilotRuntimeClient(runtimeClient) {
|
|
125
|
+
await stopCopilotClient(runtimeClient.client);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Executes one OpenAI-compatible request through a fresh Copilot session.
|
|
61
129
|
*/
|
|
62
130
|
async function sendCopilotChatCompletion(args) {
|
|
63
|
-
const
|
|
131
|
+
const ownsClient = !args.runtimeClient;
|
|
132
|
+
const runtimeClient = args.runtimeClient ?? await createCopilotRuntimeClient(args.runtimesDir);
|
|
133
|
+
if (ownsClient) {
|
|
134
|
+
await startCopilotRuntimeClient(runtimeClient);
|
|
135
|
+
}
|
|
136
|
+
let session = null;
|
|
64
137
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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);
|
|
138
|
+
session = await createCopilotSession(runtimeClient, args.payload);
|
|
139
|
+
const prompt = buildPrompt(args.payload);
|
|
140
|
+
const timeoutMs = args.timeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS;
|
|
141
|
+
const result = await sendSessionRequest(session, prompt, timeoutMs, args.onStreamEvent);
|
|
142
|
+
const content = extractCopilotContent(result);
|
|
88
143
|
return {
|
|
89
144
|
id: `copilot-${Date.now()}`,
|
|
90
145
|
object: "chat.completion",
|
|
91
146
|
created: Math.floor(Date.now() / 1000),
|
|
92
|
-
model: args.payload.model
|
|
147
|
+
model: typeof args.payload.model === "string" ? args.payload.model : "copilot",
|
|
93
148
|
choices: [
|
|
94
149
|
{
|
|
95
150
|
index: 0,
|
|
@@ -103,27 +158,36 @@ async function sendCopilotChatCompletion(args) {
|
|
|
103
158
|
};
|
|
104
159
|
}
|
|
105
160
|
finally {
|
|
106
|
-
|
|
161
|
+
if (session) {
|
|
162
|
+
await disconnectCopilotSession(session);
|
|
163
|
+
}
|
|
164
|
+
if (ownsClient) {
|
|
165
|
+
await stopCopilotRuntimeClient(runtimeClient);
|
|
166
|
+
}
|
|
107
167
|
}
|
|
108
168
|
}
|
|
109
|
-
async function createCopilotSession(
|
|
110
|
-
const
|
|
111
|
-
const client = createCopilotClient(sdk);
|
|
112
|
-
const createSession = resolveCallable(client ? client : null, "createSession") ?? resolveCallable(sdk, "createSession");
|
|
169
|
+
async function createCopilotSession(runtimeClient, payload) {
|
|
170
|
+
const createSession = resolveCallable(runtimeClient.client, "createSession");
|
|
113
171
|
if (!createSession) {
|
|
114
|
-
throw (0, errors_1.cliError)("
|
|
172
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose CopilotClient.createSession().", {});
|
|
115
173
|
}
|
|
116
174
|
try {
|
|
117
|
-
const session =
|
|
118
|
-
|
|
119
|
-
sdk,
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
175
|
+
const session = await Promise.resolve(createSession({
|
|
176
|
+
model: typeof payload.model === "string" ? payload.model : undefined,
|
|
177
|
+
...createSessionOptions(runtimeClient.sdk),
|
|
178
|
+
}));
|
|
179
|
+
if (!session || typeof session !== "object") {
|
|
180
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK returned an invalid CopilotSession.", {});
|
|
181
|
+
}
|
|
182
|
+
assertCopilotSessionContract(session);
|
|
183
|
+
return session;
|
|
123
184
|
}
|
|
124
185
|
catch (error) {
|
|
186
|
+
if (isCliError(error)) {
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
125
189
|
if (classifyCopilotSessionError(error) === "unsupported") {
|
|
126
|
-
throw (0, errors_1.cliError)("
|
|
190
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose a compatible session API.", {
|
|
127
191
|
cause: error instanceof Error ? error.message : String(error),
|
|
128
192
|
});
|
|
129
193
|
}
|
|
@@ -132,9 +196,61 @@ async function createCopilotSession(runtimesDir) {
|
|
|
132
196
|
});
|
|
133
197
|
}
|
|
134
198
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
199
|
+
async function sendSessionRequest(session, prompt, timeoutMs, onStreamEvent) {
|
|
200
|
+
const sendAndWait = resolveCallable(session, "sendAndWait");
|
|
201
|
+
const send = resolveCallable(session, "send");
|
|
202
|
+
if (!sendAndWait && !send) {
|
|
203
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK session does not expose sendAndWait() or send().", {});
|
|
204
|
+
}
|
|
205
|
+
const deltaHandler = (event) => {
|
|
206
|
+
const delta = extractDelta(event);
|
|
207
|
+
if (delta) {
|
|
208
|
+
onStreamEvent?.({ type: "delta", delta });
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
if (onStreamEvent && session.on) {
|
|
212
|
+
session.on("data", deltaHandler);
|
|
213
|
+
session.on("message", deltaHandler);
|
|
214
|
+
session.on("delta", deltaHandler);
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const sendPromise = Promise.resolve(sendAndWait ? sendAndWait({ prompt }, timeoutMs) : send({ prompt }));
|
|
218
|
+
const result = await withTimeout(sendPromise, timeoutMs, async () => {
|
|
219
|
+
await abortCopilotSession(session);
|
|
220
|
+
});
|
|
221
|
+
if (onStreamEvent) {
|
|
222
|
+
onStreamEvent({ type: "done", text: extractCopilotContent(result) });
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
if (onStreamEvent && session.off) {
|
|
228
|
+
session.off("data", deltaHandler);
|
|
229
|
+
session.off("message", deltaHandler);
|
|
230
|
+
session.off("delta", deltaHandler);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function withTimeout(promise, timeoutMs, onTimeout) {
|
|
235
|
+
let timeout = null;
|
|
236
|
+
try {
|
|
237
|
+
return await Promise.race([
|
|
238
|
+
promise,
|
|
239
|
+
new Promise((_resolve, reject) => {
|
|
240
|
+
timeout = setTimeout(() => {
|
|
241
|
+
void onTimeout().finally(() => {
|
|
242
|
+
reject((0, errors_1.cliError)("BRIDGE_UPSTREAM_TIMEOUT", "Copilot upstream request timed out.", { timeoutMs }));
|
|
243
|
+
});
|
|
244
|
+
}, timeoutMs);
|
|
245
|
+
}),
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
if (timeout) {
|
|
250
|
+
clearTimeout(timeout);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
138
254
|
function createSessionOptions(sdk) {
|
|
139
255
|
const approveAll = resolveApproveAll(sdk);
|
|
140
256
|
if (approveAll) {
|
|
@@ -142,35 +258,162 @@ function createSessionOptions(sdk) {
|
|
|
142
258
|
onPermissionRequest: (request) => approveAll(request),
|
|
143
259
|
};
|
|
144
260
|
}
|
|
145
|
-
|
|
146
|
-
onPermissionRequest: () => true,
|
|
147
|
-
};
|
|
261
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose an approveAll permission handler.", {});
|
|
148
262
|
}
|
|
149
|
-
function createCopilotClient(sdk) {
|
|
263
|
+
function createCopilotClient(sdk, runtimesDir) {
|
|
150
264
|
const ClientCtor = resolveConstructor(sdk, "CopilotClient");
|
|
151
265
|
if (!ClientCtor) {
|
|
152
|
-
|
|
266
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose CopilotClient.", {});
|
|
267
|
+
}
|
|
268
|
+
const runtimeConnection = resolveRuntimeConnectionFactory(sdk);
|
|
269
|
+
if (!runtimeConnection?.forStdio) {
|
|
270
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose RuntimeConnection.forStdio().", {});
|
|
271
|
+
}
|
|
272
|
+
const runtimeInvocation = (0, copilot_cli_1.resolveCopilotSdkRuntimeInvocation)(runtimesDir);
|
|
273
|
+
if (!runtimeInvocation) {
|
|
274
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot runtime is missing the @github/copilot npm loader required by the SDK.", {
|
|
275
|
+
expectedRuntimeFile: "node_modules/@github/copilot/npm-loader.js",
|
|
276
|
+
});
|
|
153
277
|
}
|
|
154
278
|
try {
|
|
155
|
-
return new ClientCtor(
|
|
279
|
+
return new ClientCtor({
|
|
280
|
+
connection: runtimeConnection.forStdio({
|
|
281
|
+
path: runtimeInvocation.path,
|
|
282
|
+
args: runtimeInvocation.args,
|
|
283
|
+
}),
|
|
284
|
+
});
|
|
156
285
|
}
|
|
157
286
|
catch (error) {
|
|
158
|
-
throw (0, errors_1.cliError)("
|
|
287
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK CopilotClient could not be constructed.", {
|
|
159
288
|
cause: error instanceof Error ? error.message : String(error),
|
|
160
289
|
});
|
|
161
290
|
}
|
|
162
291
|
}
|
|
292
|
+
function assertCopilotSdkApiContract(sdk, client) {
|
|
293
|
+
const createSession = resolveCallable(client, "createSession");
|
|
294
|
+
const getAuthStatus = resolveCallable(client, "getAuthStatus");
|
|
295
|
+
if (!createSession || !getAuthStatus) {
|
|
296
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose the required CopilotClient API.", {
|
|
297
|
+
hasCreateSession: Boolean(createSession),
|
|
298
|
+
hasGetAuthStatus: Boolean(getAuthStatus),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
if (!resolveApproveAll(sdk)) {
|
|
302
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK does not expose a supported permission handler.", {});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function assertCopilotSessionContract(session) {
|
|
306
|
+
const hasSendAndWait = typeof session.sendAndWait === "function";
|
|
307
|
+
const hasSend = typeof session.send === "function";
|
|
308
|
+
const hasAbort = typeof session.abort === "function";
|
|
309
|
+
if ((!hasSendAndWait && !hasSend) || !hasAbort) {
|
|
310
|
+
throw (0, errors_1.cliError)("COPILOT_SDK_API_UNSUPPORTED", "The installed Copilot SDK session does not expose the required request API.", {
|
|
311
|
+
hasSendAndWait,
|
|
312
|
+
hasSend,
|
|
313
|
+
hasAbort,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async function startCopilotClient(client) {
|
|
318
|
+
const start = resolveCallable(client, "start");
|
|
319
|
+
if (start) {
|
|
320
|
+
await Promise.resolve(start());
|
|
321
|
+
}
|
|
322
|
+
}
|
|
163
323
|
async function stopCopilotClient(client) {
|
|
164
|
-
|
|
165
|
-
|
|
324
|
+
const stop = resolveCallable(client, "stop");
|
|
325
|
+
if (stop) {
|
|
326
|
+
await Promise.resolve(stop());
|
|
166
327
|
}
|
|
167
328
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
329
|
+
async function abortCopilotSession(session) {
|
|
330
|
+
const abort = resolveCallable(session, "abort");
|
|
331
|
+
if (abort) {
|
|
332
|
+
await Promise.resolve(abort());
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
async function disconnectCopilotSession(session) {
|
|
336
|
+
const disconnect = resolveCallable(session, "disconnect");
|
|
337
|
+
if (disconnect) {
|
|
338
|
+
await Promise.resolve(disconnect());
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function buildPrompt(payload) {
|
|
342
|
+
if (Array.isArray(payload.messages)) {
|
|
343
|
+
return payload.messages
|
|
344
|
+
.map((entry) => {
|
|
345
|
+
const message = entry;
|
|
346
|
+
return `${String(message.role ?? "user")}: ${String(message.content ?? "")}`;
|
|
347
|
+
})
|
|
348
|
+
.join("\n");
|
|
349
|
+
}
|
|
350
|
+
if (typeof payload.prompt === "string") {
|
|
351
|
+
return payload.prompt;
|
|
352
|
+
}
|
|
353
|
+
return "";
|
|
354
|
+
}
|
|
355
|
+
function extractCopilotContent(result) {
|
|
356
|
+
if (typeof result === "string") {
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
if (!result || typeof result !== "object") {
|
|
360
|
+
return JSON.stringify(result);
|
|
361
|
+
}
|
|
362
|
+
const record = result;
|
|
363
|
+
if (typeof record.content === "string") {
|
|
364
|
+
return record.content;
|
|
365
|
+
}
|
|
366
|
+
if (typeof record.text === "string") {
|
|
367
|
+
return record.text;
|
|
368
|
+
}
|
|
369
|
+
if (typeof record.message === "string") {
|
|
370
|
+
return record.message;
|
|
371
|
+
}
|
|
372
|
+
if (record.data && typeof record.data === "object") {
|
|
373
|
+
const data = record.data;
|
|
374
|
+
if (typeof data.content === "string") {
|
|
375
|
+
return data.content;
|
|
376
|
+
}
|
|
377
|
+
if (typeof data.text === "string") {
|
|
378
|
+
return data.text;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return JSON.stringify(result);
|
|
382
|
+
}
|
|
383
|
+
function extractDelta(event) {
|
|
384
|
+
if (typeof event === "string") {
|
|
385
|
+
return event;
|
|
386
|
+
}
|
|
387
|
+
if (!event || typeof event !== "object") {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
const record = event;
|
|
391
|
+
for (const key of ["delta", "text", "content"]) {
|
|
392
|
+
if (typeof record[key] === "string") {
|
|
393
|
+
return record[key];
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
function isAuthReady(status) {
|
|
399
|
+
if (status === true) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
if (!status || typeof status !== "object") {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
const record = status;
|
|
406
|
+
if (record.ready === true || record.isAuthenticated === true || record.authenticated === true) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
if (typeof record.status === "string" && /^(ok|ready|authenticated|logged_in)$/i.test(record.status)) {
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
171
414
|
function classifyCopilotSessionError(error) {
|
|
172
415
|
const message = error instanceof Error ? error.message : String(error);
|
|
173
|
-
if (/onPermissionRequest
|
|
416
|
+
if (/onPermissionRequest|permission|sendAndWait|createSession|unsupported/i.test(message)) {
|
|
174
417
|
return "unsupported";
|
|
175
418
|
}
|
|
176
419
|
return "auth";
|
|
@@ -200,17 +443,50 @@ function resolveConstructor(target, name) {
|
|
|
200
443
|
}
|
|
201
444
|
return null;
|
|
202
445
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
446
|
+
function resolveRuntimeConnectionFactory(target) {
|
|
447
|
+
const direct = target.RuntimeConnection;
|
|
448
|
+
if (direct && typeof direct === "object") {
|
|
449
|
+
return direct;
|
|
450
|
+
}
|
|
451
|
+
const nestedDefault = target.default;
|
|
452
|
+
if (nestedDefault) {
|
|
453
|
+
return resolveRuntimeConnectionFactory(nestedDefault);
|
|
454
|
+
}
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
206
457
|
function resolveApproveAll(target) {
|
|
207
458
|
const direct = target.approveAll;
|
|
208
459
|
if (typeof direct === "function") {
|
|
209
460
|
return direct;
|
|
210
461
|
}
|
|
462
|
+
const permissionHandler = target.PermissionHandler;
|
|
463
|
+
if (permissionHandler && typeof permissionHandler === "object" && typeof permissionHandler.approveAll === "function") {
|
|
464
|
+
return permissionHandler.approveAll;
|
|
465
|
+
}
|
|
211
466
|
const nestedDefault = target.default;
|
|
212
|
-
if (nestedDefault
|
|
213
|
-
return nestedDefault
|
|
467
|
+
if (nestedDefault) {
|
|
468
|
+
return resolveApproveAll(nestedDefault);
|
|
214
469
|
}
|
|
215
470
|
return null;
|
|
216
471
|
}
|
|
472
|
+
function isCliError(error) {
|
|
473
|
+
return Boolean(error && typeof error === "object" && typeof error.code === "string");
|
|
474
|
+
}
|
|
475
|
+
function safeCopilotNodeRuntimeStatus() {
|
|
476
|
+
try {
|
|
477
|
+
(0, copilot_installer_1.assertCopilotNodeRuntimeSupported)();
|
|
478
|
+
return { ok: true };
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
return {
|
|
482
|
+
ok: false,
|
|
483
|
+
availability: {
|
|
484
|
+
ok: false,
|
|
485
|
+
runtime: "copilot-sdk",
|
|
486
|
+
reason: "unsupported",
|
|
487
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
488
|
+
details: isCliError(error) ? error.details : undefined,
|
|
489
|
+
},
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
}
|
|
@@ -2,19 +2,44 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const copilot_bridge_1 = require("./copilot-bridge");
|
|
4
4
|
const copilot_adapter_1 = require("./copilot-adapter");
|
|
5
|
+
let requestQueue = Promise.resolve();
|
|
6
|
+
function enqueueRequest(task) {
|
|
7
|
+
const run = requestQueue.then(task, task);
|
|
8
|
+
requestQueue = run.catch(() => undefined);
|
|
9
|
+
return run;
|
|
10
|
+
}
|
|
5
11
|
async function main() {
|
|
6
12
|
const provider = process.env.CODEX_SWITCH_BRIDGE_PROVIDER ?? "copilot";
|
|
7
13
|
const host = process.env.CODEX_SWITCH_BRIDGE_HOST ?? "127.0.0.1";
|
|
8
14
|
const port = Number(process.env.CODEX_SWITCH_BRIDGE_PORT ?? "41415");
|
|
9
15
|
const apiKey = process.env.CODEX_SWITCH_BRIDGE_API_KEY ?? "";
|
|
16
|
+
const runtimesDir = process.env.CODEX_SWITCH_RUNTIMES_DIR || undefined;
|
|
17
|
+
const runtimeClient = await (0, copilot_adapter_1.createCopilotRuntimeClient)(runtimesDir);
|
|
18
|
+
await (0, copilot_adapter_1.startCopilotRuntimeClient)(runtimeClient);
|
|
19
|
+
const stopRuntime = () => {
|
|
20
|
+
void (0, copilot_adapter_1.stopCopilotRuntimeClient)(runtimeClient).finally(() => process.exit(0));
|
|
21
|
+
};
|
|
22
|
+
process.once("SIGINT", stopRuntime);
|
|
23
|
+
process.once("SIGTERM", stopRuntime);
|
|
10
24
|
await (0, copilot_bridge_1.startCopilotBridgeServer)({
|
|
11
25
|
host,
|
|
12
26
|
port,
|
|
13
27
|
apiKey,
|
|
14
|
-
executeChatCompletion: async (payload) => (0, copilot_adapter_1.sendCopilotChatCompletion)({
|
|
28
|
+
executeChatCompletion: async (payload, options) => enqueueRequest(() => (0, copilot_adapter_1.sendCopilotChatCompletion)({
|
|
15
29
|
provider,
|
|
16
30
|
payload,
|
|
17
|
-
|
|
31
|
+
runtimesDir,
|
|
32
|
+
runtimeClient,
|
|
33
|
+
timeoutMs: options?.timeoutMs,
|
|
34
|
+
onStreamEvent: (event) => {
|
|
35
|
+
if (event.type === "delta") {
|
|
36
|
+
options?.onTextDelta?.(event.delta);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
options?.onTextDone?.(event.text);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
})),
|
|
18
43
|
});
|
|
19
44
|
}
|
|
20
45
|
if (require.main === module) {
|