@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.
- package/README.AI.md +66 -94
- package/README.CN.md +84 -139
- package/README.md +91 -151
- package/dist/app/add-provider.js +6 -83
- package/dist/app/edit-provider.js +9 -29
- package/dist/app/get-status.js +1 -77
- package/dist/app/list-providers.js +0 -2
- package/dist/app/remove-provider.js +3 -5
- package/dist/app/run-doctor.js +2 -99
- package/dist/app/setup-codex.js +0 -2
- package/dist/app/switch-provider.js +1 -74
- package/dist/cli/output.js +3 -89
- package/dist/commands/handlers.js +20 -172
- package/dist/commands/help.js +1 -4
- package/dist/commands/registry.js +6 -74
- package/dist/domain/config.js +1 -3
- package/dist/domain/providers.js +4 -92
- package/dist/domain/runtime-state.js +0 -88
- package/dist/interaction/add-interactive.js +1 -55
- package/dist/interaction/interactive.js +1 -3
- package/dist/runtime/codex-probe.js +0 -12
- package/dist/storage/codex-paths.js +0 -2
- package/docs/Design/codex-switch-v0.2.1-design.md +77 -0
- package/docs/PRD/codex-switch-prd-v0.2.1.md +82 -0
- package/docs/Tests/testing.md +32 -34
- package/docs/cli-usage.md +67 -235
- package/docs/codex-switch-command-design.md +1 -1
- package/docs/codex-switch-product-overview.md +49 -96
- package/docs/codex-switch-technical-architecture.md +37 -52
- package/package.json +1 -1
- package/dist/app/bridge.js +0 -303
- package/dist/runtime/copilot-adapter.js +0 -617
- package/dist/runtime/copilot-bridge-worker.js +0 -69
- package/dist/runtime/copilot-bridge.js +0 -1351
- package/dist/runtime/copilot-cli.js +0 -164
- package/dist/runtime/copilot-http-bridge-worker.js +0 -228
- package/dist/runtime/copilot-installer.js +0 -231
- package/dist/runtime/copilot-sdk-loader.js +0 -62
- package/dist/runtime/copilot-token.js +0 -294
- package/dist/storage/runtime-state-repo.js +0 -121
|
@@ -1,294 +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.setCopilotTokenExchangeImplementation = setCopilotTokenExchangeImplementation;
|
|
37
|
-
exports.resetCopilotTokenExchangeImplementation = resetCopilotTokenExchangeImplementation;
|
|
38
|
-
exports.getCopilotRequestHeaders = getCopilotRequestHeaders;
|
|
39
|
-
exports.getGithubTokenPath = getGithubTokenPath;
|
|
40
|
-
exports.readGithubToken = readGithubToken;
|
|
41
|
-
exports.writeGithubToken = writeGithubToken;
|
|
42
|
-
exports.startDeviceFlow = startDeviceFlow;
|
|
43
|
-
exports.pollDeviceFlowToken = pollDeviceFlowToken;
|
|
44
|
-
exports.exchangeForCopilotToken = exchangeForCopilotToken;
|
|
45
|
-
exports.createTokenManager = createTokenManager;
|
|
46
|
-
exports.createStaticTokenManager = createStaticTokenManager;
|
|
47
|
-
const https = __importStar(require("node:https"));
|
|
48
|
-
const fs = __importStar(require("node:fs"));
|
|
49
|
-
const path = __importStar(require("node:path"));
|
|
50
|
-
const crypto = __importStar(require("node:crypto"));
|
|
51
|
-
const codex_paths_1 = require("../storage/codex-paths");
|
|
52
|
-
const errors_1 = require("../domain/errors");
|
|
53
|
-
const GITHUB_OAUTH_CLIENT_ID = "Iv1.b507a08c87ecfe98";
|
|
54
|
-
const GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
55
|
-
const GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
56
|
-
const COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
|
|
57
|
-
const EDITOR_VERSION = "vscode/1.100.0";
|
|
58
|
-
const COPILOT_CHAT_VERSION = "copilot-chat/0.30.0";
|
|
59
|
-
const USER_AGENT = "GitHubCopilotChat/0.30.0";
|
|
60
|
-
let exchangeImplementation = null;
|
|
61
|
-
function setCopilotTokenExchangeImplementation(impl) {
|
|
62
|
-
exchangeImplementation = impl;
|
|
63
|
-
}
|
|
64
|
-
function resetCopilotTokenExchangeImplementation() {
|
|
65
|
-
exchangeImplementation = null;
|
|
66
|
-
}
|
|
67
|
-
const SESSION_ID = crypto.randomUUID();
|
|
68
|
-
const MACHINE_ID = crypto.randomBytes(32).toString("hex");
|
|
69
|
-
function getCopilotRequestHeaders(copilotToken, requestId) {
|
|
70
|
-
return {
|
|
71
|
-
"authorization": `Bearer ${copilotToken}`,
|
|
72
|
-
"content-type": "application/json",
|
|
73
|
-
"copilot-integration-id": "vscode-chat",
|
|
74
|
-
"editor-version": EDITOR_VERSION,
|
|
75
|
-
"editor-plugin-version": COPILOT_CHAT_VERSION,
|
|
76
|
-
"user-agent": USER_AGENT,
|
|
77
|
-
"openai-intent": "conversation-panel",
|
|
78
|
-
"x-interaction-type": "conversation-panel",
|
|
79
|
-
"x-github-api-version": "2026-01-09",
|
|
80
|
-
"x-request-id": requestId ?? crypto.randomUUID(),
|
|
81
|
-
"vscode-sessionid": SESSION_ID,
|
|
82
|
-
"vscode-machineid": MACHINE_ID,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function getGithubTokenPath(toolHomeDir) {
|
|
86
|
-
const home = (0, codex_paths_1.resolveCodexSwitchHome)(toolHomeDir);
|
|
87
|
-
return path.join(home, "github-token");
|
|
88
|
-
}
|
|
89
|
-
function readGithubToken(toolHomeDir) {
|
|
90
|
-
const tokenPath = getGithubTokenPath(toolHomeDir);
|
|
91
|
-
if (!fs.existsSync(tokenPath)) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
return fs.readFileSync(tokenPath, "utf8").trim();
|
|
95
|
-
}
|
|
96
|
-
function writeGithubToken(token, toolHomeDir) {
|
|
97
|
-
const tokenPath = getGithubTokenPath(toolHomeDir);
|
|
98
|
-
fs.mkdirSync(path.dirname(tokenPath), { recursive: true });
|
|
99
|
-
fs.writeFileSync(tokenPath, token, "utf8");
|
|
100
|
-
}
|
|
101
|
-
async function startDeviceFlow() {
|
|
102
|
-
const body = `client_id=${GITHUB_OAUTH_CLIENT_ID}&scope=read:user`;
|
|
103
|
-
const response = await httpsPost(GITHUB_DEVICE_CODE_URL, body, {
|
|
104
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
105
|
-
"accept": "application/json",
|
|
106
|
-
});
|
|
107
|
-
if (!response.ok) {
|
|
108
|
-
throw (0, errors_1.cliError)("GITHUB_DEVICE_FLOW_FAILED", `GitHub device flow initiation failed: ${response.status}`, {
|
|
109
|
-
status: response.status,
|
|
110
|
-
body: response.body,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
const data = JSON.parse(response.body);
|
|
114
|
-
return {
|
|
115
|
-
userCode: String(data.user_code ?? ""),
|
|
116
|
-
verificationUri: String(data.verification_uri ?? "https://github.com/login/device"),
|
|
117
|
-
deviceCode: String(data.device_code ?? ""),
|
|
118
|
-
interval: Number(data.interval ?? 5),
|
|
119
|
-
expiresIn: Number(data.expires_in ?? 900),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
async function pollDeviceFlowToken(deviceCode, interval, expiresIn) {
|
|
123
|
-
const deadline = Date.now() + expiresIn * 1000;
|
|
124
|
-
let pollInterval = interval;
|
|
125
|
-
while (Date.now() < deadline) {
|
|
126
|
-
await sleep(pollInterval * 1000);
|
|
127
|
-
const body = `client_id=${GITHUB_OAUTH_CLIENT_ID}&device_code=${deviceCode}&grant_type=urn:ietf:params:oauth:grant-type:device_code`;
|
|
128
|
-
const response = await httpsPost(GITHUB_ACCESS_TOKEN_URL, body, {
|
|
129
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
130
|
-
"accept": "application/json",
|
|
131
|
-
});
|
|
132
|
-
if (!response.ok) {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
const data = JSON.parse(response.body);
|
|
136
|
-
if (data.access_token && typeof data.access_token === "string") {
|
|
137
|
-
return data.access_token;
|
|
138
|
-
}
|
|
139
|
-
const error = String(data.error ?? "");
|
|
140
|
-
if (error === "authorization_pending") {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
if (error === "slow_down") {
|
|
144
|
-
pollInterval += 5;
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (error === "expired_token" || error === "access_denied") {
|
|
148
|
-
throw (0, errors_1.cliError)("GITHUB_DEVICE_FLOW_FAILED", `GitHub device flow failed: ${error}`, { error });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
throw (0, errors_1.cliError)("GITHUB_DEVICE_FLOW_FAILED", "GitHub device flow timed out waiting for user authorization.", {});
|
|
152
|
-
}
|
|
153
|
-
async function exchangeForCopilotToken(githubPat) {
|
|
154
|
-
if (exchangeImplementation) {
|
|
155
|
-
return exchangeImplementation(githubPat);
|
|
156
|
-
}
|
|
157
|
-
const response = await httpsGet(COPILOT_TOKEN_URL, {
|
|
158
|
-
"authorization": `token ${githubPat}`,
|
|
159
|
-
"content-type": "application/json",
|
|
160
|
-
"accept": "application/json",
|
|
161
|
-
"editor-version": EDITOR_VERSION,
|
|
162
|
-
"editor-plugin-version": COPILOT_CHAT_VERSION,
|
|
163
|
-
"user-agent": USER_AGENT,
|
|
164
|
-
"x-github-api-version": "2026-01-09",
|
|
165
|
-
});
|
|
166
|
-
if (!response.ok) {
|
|
167
|
-
if (response.status === 401) {
|
|
168
|
-
throw (0, errors_1.cliError)("COPILOT_AUTH_REQUIRED", "GitHub token is invalid or expired. Run `codexs login copilot` to re-authenticate.", {
|
|
169
|
-
status: response.status,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
throw (0, errors_1.cliError)("COPILOT_TOKEN_EXCHANGE_FAILED", `Failed to exchange GitHub token for Copilot token: HTTP ${response.status}`, {
|
|
173
|
-
status: response.status,
|
|
174
|
-
body: response.body,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
const data = JSON.parse(response.body);
|
|
178
|
-
const token = String(data.token ?? "");
|
|
179
|
-
if (!token) {
|
|
180
|
-
throw (0, errors_1.cliError)("COPILOT_TOKEN_EXCHANGE_FAILED", "Copilot token exchange returned empty token.", { data });
|
|
181
|
-
}
|
|
182
|
-
const endpoints = data.endpoints;
|
|
183
|
-
const apiBaseUrl = String(endpoints?.api ?? "https://api.githubcopilot.com");
|
|
184
|
-
return {
|
|
185
|
-
token,
|
|
186
|
-
expiresAt: Number(data.expires_at ?? Date.now() / 1000 + 1800),
|
|
187
|
-
apiBaseUrl: apiBaseUrl.replace(/\/$/, ""),
|
|
188
|
-
refreshIn: Number(data.refresh_in ?? 1500),
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function createTokenManager(githubPat) {
|
|
192
|
-
let currentToken = null;
|
|
193
|
-
let refreshTimer = null;
|
|
194
|
-
let refreshing = null;
|
|
195
|
-
async function refresh() {
|
|
196
|
-
currentToken = await exchangeForCopilotToken(githubPat);
|
|
197
|
-
scheduleRefresh();
|
|
198
|
-
}
|
|
199
|
-
function scheduleRefresh() {
|
|
200
|
-
if (refreshTimer) {
|
|
201
|
-
clearTimeout(refreshTimer);
|
|
202
|
-
}
|
|
203
|
-
const delayMs = Math.max((currentToken.refreshIn - 60) * 1000, 30000);
|
|
204
|
-
refreshTimer = setTimeout(() => {
|
|
205
|
-
refreshing = refresh().catch(() => {
|
|
206
|
-
// retry after 30s on failure
|
|
207
|
-
refreshTimer = setTimeout(() => { refreshing = refresh(); }, 30000);
|
|
208
|
-
});
|
|
209
|
-
}, delayMs);
|
|
210
|
-
refreshTimer.unref();
|
|
211
|
-
}
|
|
212
|
-
return {
|
|
213
|
-
async getToken() {
|
|
214
|
-
if (!currentToken || Date.now() / 1000 >= currentToken.expiresAt - 60) {
|
|
215
|
-
if (!refreshing) {
|
|
216
|
-
refreshing = refresh();
|
|
217
|
-
}
|
|
218
|
-
await refreshing;
|
|
219
|
-
refreshing = null;
|
|
220
|
-
}
|
|
221
|
-
return currentToken.token;
|
|
222
|
-
},
|
|
223
|
-
getApiBaseUrl() {
|
|
224
|
-
return currentToken?.apiBaseUrl ?? "https://api.githubcopilot.com";
|
|
225
|
-
},
|
|
226
|
-
invalidate() {
|
|
227
|
-
currentToken = null;
|
|
228
|
-
},
|
|
229
|
-
stop() {
|
|
230
|
-
if (refreshTimer) {
|
|
231
|
-
clearTimeout(refreshTimer);
|
|
232
|
-
refreshTimer = null;
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
function createStaticTokenManager(token) {
|
|
238
|
-
return {
|
|
239
|
-
async getToken() {
|
|
240
|
-
return token;
|
|
241
|
-
},
|
|
242
|
-
getApiBaseUrl() {
|
|
243
|
-
return "https://api.githubcopilot.com";
|
|
244
|
-
},
|
|
245
|
-
invalidate() { },
|
|
246
|
-
stop() { },
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
function httpsPost(url, body, headers) {
|
|
250
|
-
return new Promise((resolve, reject) => {
|
|
251
|
-
const parsed = new URL(url);
|
|
252
|
-
const req = https.request({
|
|
253
|
-
hostname: parsed.hostname,
|
|
254
|
-
port: parsed.port || 443,
|
|
255
|
-
path: parsed.pathname + parsed.search,
|
|
256
|
-
method: "POST",
|
|
257
|
-
headers: { ...headers, "content-length": Buffer.byteLength(body).toString() },
|
|
258
|
-
}, (res) => {
|
|
259
|
-
const chunks = [];
|
|
260
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
261
|
-
res.on("end", () => {
|
|
262
|
-
const responseBody = Buffer.concat(chunks).toString("utf8");
|
|
263
|
-
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, body: responseBody });
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
req.on("error", reject);
|
|
267
|
-
req.write(body);
|
|
268
|
-
req.end();
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
function httpsGet(url, headers) {
|
|
272
|
-
return new Promise((resolve, reject) => {
|
|
273
|
-
const parsed = new URL(url);
|
|
274
|
-
const req = https.request({
|
|
275
|
-
hostname: parsed.hostname,
|
|
276
|
-
port: parsed.port || 443,
|
|
277
|
-
path: parsed.pathname + parsed.search,
|
|
278
|
-
method: "GET",
|
|
279
|
-
headers,
|
|
280
|
-
}, (res) => {
|
|
281
|
-
const chunks = [];
|
|
282
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
283
|
-
res.on("end", () => {
|
|
284
|
-
const responseBody = Buffer.concat(chunks).toString("utf8");
|
|
285
|
-
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, body: responseBody });
|
|
286
|
-
});
|
|
287
|
-
});
|
|
288
|
-
req.on("error", reject);
|
|
289
|
-
req.end();
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
function sleep(ms) {
|
|
293
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
294
|
-
}
|
|
@@ -1,121 +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.getCopilotBridgeStatePath = getCopilotBridgeStatePath;
|
|
37
|
-
exports.getCopilotBridgeLogPath = getCopilotBridgeLogPath;
|
|
38
|
-
exports.readCopilotBridgeState = readCopilotBridgeState;
|
|
39
|
-
exports.inspectCopilotBridgeState = inspectCopilotBridgeState;
|
|
40
|
-
exports.writeCopilotBridgeState = writeCopilotBridgeState;
|
|
41
|
-
exports.clearCopilotBridgeState = clearCopilotBridgeState;
|
|
42
|
-
const fs = __importStar(require("node:fs"));
|
|
43
|
-
const path = __importStar(require("node:path"));
|
|
44
|
-
const errors_1 = require("../domain/errors");
|
|
45
|
-
const codex_paths_1 = require("./codex-paths");
|
|
46
|
-
const fs_utils_1 = require("./fs-utils");
|
|
47
|
-
/**
|
|
48
|
-
* Returns the tool-home runtime state file used by Copilot bridge helpers.
|
|
49
|
-
*/
|
|
50
|
-
function getCopilotBridgeStatePath(runtimeDir) {
|
|
51
|
-
const override = process.env.CODEX_SWITCH_RUNTIME_STATE_DIR;
|
|
52
|
-
if (override && override.trim() !== "") {
|
|
53
|
-
return path.join(path.resolve(override), "copilot-bridge-state.json");
|
|
54
|
-
}
|
|
55
|
-
const baseRuntimeDir = runtimeDir ? path.resolve(runtimeDir) : path.join((0, codex_paths_1.resolveCodexSwitchHome)(), "runtime");
|
|
56
|
-
return path.join(baseRuntimeDir, "copilot-bridge-state.json");
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Returns the persisted bridge runtime log path colocated with the bridge state manifest.
|
|
60
|
-
*/
|
|
61
|
-
function getCopilotBridgeLogPath(runtimeDir) {
|
|
62
|
-
const statePath = getCopilotBridgeStatePath(runtimeDir);
|
|
63
|
-
return path.join(path.dirname(statePath), "copilot-bridge.log");
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Reads the Copilot bridge state manifest when present.
|
|
67
|
-
*/
|
|
68
|
-
function readCopilotBridgeState(runtimeDir) {
|
|
69
|
-
const statePath = getCopilotBridgeStatePath(runtimeDir);
|
|
70
|
-
if (!fs.existsSync(statePath)) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
return JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Safely inspects the runtime-state file for status/doctor style read paths.
|
|
77
|
-
*/
|
|
78
|
-
function inspectCopilotBridgeState(runtimeDir) {
|
|
79
|
-
const statePath = getCopilotBridgeStatePath(runtimeDir);
|
|
80
|
-
if (!fs.existsSync(statePath)) {
|
|
81
|
-
return {
|
|
82
|
-
exists: false,
|
|
83
|
-
valid: false,
|
|
84
|
-
parseError: null,
|
|
85
|
-
state: null,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
try {
|
|
89
|
-
return {
|
|
90
|
-
exists: true,
|
|
91
|
-
valid: true,
|
|
92
|
-
parseError: null,
|
|
93
|
-
state: readCopilotBridgeState(runtimeDir),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
catch (error) {
|
|
97
|
-
return {
|
|
98
|
-
exists: true,
|
|
99
|
-
valid: false,
|
|
100
|
-
parseError: (0, errors_1.normalizeError)(error).message,
|
|
101
|
-
state: null,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Persists the Copilot bridge state manifest.
|
|
107
|
-
*/
|
|
108
|
-
function writeCopilotBridgeState(state, runtimeDir) {
|
|
109
|
-
const statePath = getCopilotBridgeStatePath(runtimeDir);
|
|
110
|
-
(0, fs_utils_1.ensureDir)(path.dirname(statePath));
|
|
111
|
-
(0, fs_utils_1.writeTextFileAtomic)(statePath, `${JSON.stringify(state, null, 2)}\n`);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Deletes the Copilot bridge state manifest when present.
|
|
115
|
-
*/
|
|
116
|
-
function clearCopilotBridgeState(runtimeDir) {
|
|
117
|
-
const statePath = getCopilotBridgeStatePath(runtimeDir);
|
|
118
|
-
if (fs.existsSync(statePath)) {
|
|
119
|
-
fs.rmSync(statePath, { force: true });
|
|
120
|
-
}
|
|
121
|
-
}
|