opencode-copilot-account-switcher 0.13.4 → 0.13.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codex-network-retry.d.ts +2 -0
- package/dist/codex-network-retry.js +9 -0
- package/dist/codex-store.js +11 -8
- package/dist/common-settings-actions.d.ts +15 -0
- package/dist/common-settings-actions.js +42 -0
- package/dist/common-settings-store.d.ts +20 -0
- package/dist/common-settings-store.js +122 -0
- package/dist/plugin-actions.d.ts +9 -0
- package/dist/plugin-actions.js +15 -35
- package/dist/plugin-hooks.d.ts +5 -0
- package/dist/plugin-hooks.js +165 -43
- package/dist/plugin.js +54 -33
- package/dist/providers/codex-menu-adapter.d.ts +5 -2
- package/dist/providers/codex-menu-adapter.js +21 -0
- package/dist/providers/copilot-menu-adapter.d.ts +3 -0
- package/dist/providers/copilot-menu-adapter.js +5 -0
- package/dist/providers/descriptor.d.ts +3 -2
- package/dist/providers/descriptor.js +4 -1
- package/dist/providers/registry.d.ts +1 -1
- package/dist/providers/registry.js +38 -2
- package/dist/retry/codex-policy.d.ts +5 -0
- package/dist/retry/codex-policy.js +75 -0
- package/dist/retry/common-policy.d.ts +37 -0
- package/dist/retry/common-policy.js +68 -0
- package/dist/retry/copilot-policy.d.ts +1 -10
- package/dist/retry/copilot-policy.js +24 -58
- package/dist/store-paths.d.ts +6 -0
- package/dist/store-paths.js +24 -0
- package/dist/store.js +34 -8
- package/dist/ui/menu.d.ts +2 -0
- package/dist/ui/menu.js +56 -46
- package/dist/upstream/codex-loader-adapter.d.ts +69 -0
- package/dist/upstream/codex-loader-adapter.js +55 -0
- package/dist/upstream/codex-plugin.snapshot.d.ts +13 -0
- package/dist/upstream/codex-plugin.snapshot.js +98 -0
- package/package.json +3 -1
package/dist/plugin-hooks.js
CHANGED
|
@@ -3,12 +3,15 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { createCompactionLoopSafetyBypass, createLoopSafetySystemTransform, getLoopSafetyProviderScope, } from "./loop-safety-plugin.js";
|
|
5
5
|
import { COPILOT_PROVIDER_DESCRIPTOR } from "./providers/descriptor.js";
|
|
6
|
+
import { CODEX_PROVIDER_DESCRIPTOR } from "./providers/descriptor.js";
|
|
6
7
|
import { createCopilotRetryingFetch, cleanupLongIdsForAccountSwitch, detectRateLimitEvidence, INTERNAL_SESSION_CONTEXT_KEY, } from "./copilot-network-retry.js";
|
|
8
|
+
import { createCodexRetryingFetch } from "./codex-network-retry.js";
|
|
7
9
|
import { createCopilotRetryNotifier } from "./copilot-retry-notifier.js";
|
|
8
10
|
import { resolveCopilotModelAccounts } from "./model-account-map.js";
|
|
9
11
|
import { normalizeDomain } from "./copilot-api-helpers.js";
|
|
10
12
|
import { readStoreSafe, readStoreSafeSync, writeStore } from "./store.js";
|
|
11
13
|
import { loadOfficialCopilotConfig, loadOfficialCopilotChatHeaders, } from "./upstream/copilot-loader-adapter.js";
|
|
14
|
+
import { loadOfficialCodexConfig, loadOfficialCodexChatHeaders, } from "./upstream/codex-loader-adapter.js";
|
|
12
15
|
import { createNotifyTool } from "./notify-tool.js";
|
|
13
16
|
import { createWaitTool } from "./wait-tool.js";
|
|
14
17
|
import { refreshActiveAccountQuota } from "./active-account-quota.js";
|
|
@@ -39,6 +42,9 @@ export class PolicyScopeCommandHandledError extends Error {
|
|
|
39
42
|
function isCopilotProviderID(providerID) {
|
|
40
43
|
return COPILOT_PROVIDER_DESCRIPTOR.providerIDs.includes(providerID);
|
|
41
44
|
}
|
|
45
|
+
function isCodexProviderID(providerID) {
|
|
46
|
+
return CODEX_PROVIDER_DESCRIPTOR.providerIDs.includes(providerID);
|
|
47
|
+
}
|
|
42
48
|
function isDebugEnabled() {
|
|
43
49
|
return process.env.OPENCODE_COPILOT_RETRY_DEBUG === "1";
|
|
44
50
|
}
|
|
@@ -81,6 +87,25 @@ function areExperimentalSlashCommandsEnabled(store) {
|
|
|
81
87
|
return false;
|
|
82
88
|
return true;
|
|
83
89
|
}
|
|
90
|
+
function mergeStoreWithCommonSettings(store, common) {
|
|
91
|
+
if (!store && !common)
|
|
92
|
+
return undefined;
|
|
93
|
+
return {
|
|
94
|
+
...(store ?? { accounts: {} }),
|
|
95
|
+
...(common?.loopSafetyEnabled === true || common?.loopSafetyEnabled === false
|
|
96
|
+
? { loopSafetyEnabled: common.loopSafetyEnabled }
|
|
97
|
+
: {}),
|
|
98
|
+
...(common?.loopSafetyProviderScope
|
|
99
|
+
? { loopSafetyProviderScope: common.loopSafetyProviderScope }
|
|
100
|
+
: {}),
|
|
101
|
+
...(common?.networkRetryEnabled === true || common?.networkRetryEnabled === false
|
|
102
|
+
? { networkRetryEnabled: common.networkRetryEnabled }
|
|
103
|
+
: {}),
|
|
104
|
+
...(common?.experimentalSlashCommandsEnabled === true || common?.experimentalSlashCommandsEnabled === false
|
|
105
|
+
? { experimentalSlashCommandsEnabled: common.experimentalSlashCommandsEnabled }
|
|
106
|
+
: {}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
84
109
|
async function readRequestBody(request, init) {
|
|
85
110
|
try {
|
|
86
111
|
if (typeof init?.body === "string")
|
|
@@ -422,10 +447,16 @@ function pruneTouchWriteCache(input) {
|
|
|
422
447
|
}
|
|
423
448
|
export function buildPluginHooks(input) {
|
|
424
449
|
const authProvider = input.auth.provider ?? COPILOT_PROVIDER_DESCRIPTOR.providerIDs[0] ?? "github-copilot";
|
|
425
|
-
const
|
|
450
|
+
const authLoaderMode = input.authLoaderMode
|
|
451
|
+
?? (isCopilotProviderID(authProvider) ? "copilot" : isCodexProviderID(authProvider) ? "codex" : "none");
|
|
452
|
+
const enableCopilotAuthLoader = authLoaderMode === "copilot";
|
|
453
|
+
const enableCodexAuthLoader = authLoaderMode === "codex";
|
|
454
|
+
const enableModelRouting = input.enableModelRouting ?? enableCopilotAuthLoader;
|
|
426
455
|
const compactionLoopSafetyBypass = createCompactionLoopSafetyBypass();
|
|
427
456
|
const loadStore = input.loadStore ?? readStoreSafe;
|
|
428
457
|
const loadStoreSync = input.loadStoreSync ?? readStoreSafeSync;
|
|
458
|
+
const loadCommonSettings = input.loadCommonSettings;
|
|
459
|
+
const loadCommonSettingsSync = input.loadCommonSettingsSync;
|
|
429
460
|
const persistStore = (store, meta) => {
|
|
430
461
|
if (input.writeStore)
|
|
431
462
|
return input.writeStore(store, meta);
|
|
@@ -436,9 +467,30 @@ export function buildPluginHooks(input) {
|
|
|
436
467
|
const handleCodexStatusCommandImpl = input.handleCodexStatusCommandImpl ?? handleCodexStatusCommand;
|
|
437
468
|
const handleCompactCommandImpl = input.handleCompactCommandImpl ?? handleCompactCommand;
|
|
438
469
|
const handleStopToolCommandImpl = input.handleStopToolCommandImpl ?? handleStopToolCommand;
|
|
439
|
-
const
|
|
440
|
-
|
|
441
|
-
|
|
470
|
+
const loadOfficialConfigForCopilot = (args) => {
|
|
471
|
+
if (input.loadOfficialConfig) {
|
|
472
|
+
return input.loadOfficialConfig(args);
|
|
473
|
+
}
|
|
474
|
+
return loadOfficialCopilotConfig(args);
|
|
475
|
+
};
|
|
476
|
+
const loadOfficialConfigForCodex = (args) => {
|
|
477
|
+
if (input.loadOfficialConfig) {
|
|
478
|
+
return input.loadOfficialConfig(args);
|
|
479
|
+
}
|
|
480
|
+
return loadOfficialCodexConfig({
|
|
481
|
+
getAuth: args.getAuth,
|
|
482
|
+
baseFetch: args.baseFetch,
|
|
483
|
+
version: args.version,
|
|
484
|
+
client: input.client,
|
|
485
|
+
});
|
|
486
|
+
};
|
|
487
|
+
const resolveOfficialChatHeaders = enableCopilotAuthLoader
|
|
488
|
+
? input.loadOfficialChatHeaders ?? loadOfficialCopilotChatHeaders
|
|
489
|
+
: enableCodexAuthLoader
|
|
490
|
+
? loadOfficialCodexChatHeaders
|
|
491
|
+
: (async () => async () => { });
|
|
492
|
+
const createRetryFetch = input.createRetryFetch
|
|
493
|
+
?? (enableCodexAuthLoader ? createCodexRetryingFetch : createCopilotRetryingFetch);
|
|
442
494
|
const now = input.now ?? (() => Date.now());
|
|
443
495
|
const random = input.random ?? Math.random;
|
|
444
496
|
let injectArmed = false;
|
|
@@ -464,6 +516,27 @@ export function buildPluginHooks(input) {
|
|
|
464
516
|
});
|
|
465
517
|
});
|
|
466
518
|
const getPolicyScope = (store) => getLoopSafetyProviderScope(store, policyScopeOverride);
|
|
519
|
+
const loadMergedStore = async () => {
|
|
520
|
+
const [store, common] = await Promise.all([
|
|
521
|
+
loadStore().catch(() => undefined),
|
|
522
|
+
loadCommonSettings?.().catch(() => undefined),
|
|
523
|
+
]);
|
|
524
|
+
return mergeStoreWithCommonSettings(store, common);
|
|
525
|
+
};
|
|
526
|
+
const loadMergedStoreSync = () => mergeStoreWithCommonSettings(loadStoreSync(), loadCommonSettingsSync?.());
|
|
527
|
+
const isNetworkRetryEnabled = async (retryStore) => {
|
|
528
|
+
if (loadCommonSettings) {
|
|
529
|
+
const common = await loadCommonSettings().catch(() => undefined);
|
|
530
|
+
if (common?.networkRetryEnabled === true)
|
|
531
|
+
return true;
|
|
532
|
+
if (common?.networkRetryEnabled === false)
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
if (retryStore)
|
|
536
|
+
return retryStore.networkRetryEnabled === true;
|
|
537
|
+
const store = readRetryStoreContext(await loadStore().catch(() => undefined));
|
|
538
|
+
return store?.networkRetryEnabled === true;
|
|
539
|
+
};
|
|
467
540
|
const showInjectToast = async (message, variant = "info") => {
|
|
468
541
|
await showStatusToast({
|
|
469
542
|
client: input.client,
|
|
@@ -568,6 +641,7 @@ export function buildPluginHooks(input) {
|
|
|
568
641
|
const finalHeaderCapture = new AsyncLocalStorage();
|
|
569
642
|
const getScopedAuth = async () => authOverride.getStore() ?? getAuth();
|
|
570
643
|
const providerConfig = provider;
|
|
644
|
+
const loadOfficialConfig = enableCodexAuthLoader ? loadOfficialConfigForCodex : loadOfficialConfigForCopilot;
|
|
571
645
|
const config = await loadOfficialConfig({
|
|
572
646
|
getAuth: getScopedAuth,
|
|
573
647
|
provider: providerConfig,
|
|
@@ -997,35 +1071,67 @@ export function buildPluginHooks(input) {
|
|
|
997
1071
|
}
|
|
998
1072
|
return response;
|
|
999
1073
|
};
|
|
1000
|
-
|
|
1074
|
+
const providerFetch = enableModelRouting
|
|
1075
|
+
? fetchWithModelAccount
|
|
1076
|
+
: async (request, init) => {
|
|
1077
|
+
const outbound = stripInternalSessionHeader(request, init);
|
|
1078
|
+
return config.fetch(outbound.request, outbound.init);
|
|
1079
|
+
};
|
|
1080
|
+
const networkRetryEnabled = await isNetworkRetryEnabled(retryStore);
|
|
1081
|
+
if (networkRetryEnabled !== true)
|
|
1001
1082
|
return {
|
|
1002
1083
|
...config,
|
|
1003
|
-
fetch:
|
|
1084
|
+
fetch: providerFetch,
|
|
1004
1085
|
};
|
|
1005
1086
|
return {
|
|
1006
1087
|
...config,
|
|
1007
|
-
fetch: createRetryFetch(
|
|
1088
|
+
fetch: createRetryFetch(providerFetch, {
|
|
1008
1089
|
client: input.client,
|
|
1009
1090
|
directory: input.directory,
|
|
1010
1091
|
serverUrl: input.serverUrl,
|
|
1011
|
-
lastAccountSwitchAt: retryStore
|
|
1092
|
+
lastAccountSwitchAt: retryStore?.lastAccountSwitchAt,
|
|
1012
1093
|
notifier: createCopilotRetryNotifier({
|
|
1013
1094
|
client: input.client,
|
|
1014
|
-
lastAccountSwitchAt: retryStore
|
|
1095
|
+
lastAccountSwitchAt: retryStore?.lastAccountSwitchAt,
|
|
1015
1096
|
getLastAccountSwitchAt: getLatestLastAccountSwitchAt,
|
|
1016
1097
|
clearAccountSwitchContext,
|
|
1017
1098
|
now: input.now,
|
|
1018
1099
|
}),
|
|
1019
|
-
clearAccountSwitchContext: async () => clearAccountSwitchContext(retryStore
|
|
1100
|
+
clearAccountSwitchContext: async () => clearAccountSwitchContext(retryStore?.lastAccountSwitchAt),
|
|
1020
1101
|
}),
|
|
1021
1102
|
};
|
|
1022
1103
|
};
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1104
|
+
const codexLoader = async (getAuth) => {
|
|
1105
|
+
const config = await loadOfficialConfigForCodex({
|
|
1106
|
+
getAuth: getAuth,
|
|
1107
|
+
}).catch(() => undefined);
|
|
1108
|
+
if (!config || typeof config.fetch !== "function")
|
|
1109
|
+
return {};
|
|
1110
|
+
if (await isNetworkRetryEnabled()) {
|
|
1111
|
+
return {
|
|
1112
|
+
...config,
|
|
1113
|
+
fetch: createRetryFetch(config.fetch),
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
return {
|
|
1117
|
+
...config,
|
|
1118
|
+
fetch: config.fetch,
|
|
1119
|
+
};
|
|
1120
|
+
};
|
|
1121
|
+
const officialChatHeaders = (enableCopilotAuthLoader || enableCodexAuthLoader)
|
|
1122
|
+
? resolveOfficialChatHeaders({
|
|
1123
|
+
client: input.client,
|
|
1124
|
+
directory: input.directory,
|
|
1125
|
+
})
|
|
1126
|
+
: Promise.resolve(async () => { });
|
|
1027
1127
|
const chatHeaders = async (hookInput, output) => {
|
|
1028
|
-
if (
|
|
1128
|
+
if (enableCodexAuthLoader) {
|
|
1129
|
+
if (hookInput.model.providerID !== authProvider)
|
|
1130
|
+
return;
|
|
1131
|
+
await (await officialChatHeaders)(hookInput, output);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
if (!enableCopilotAuthLoader || !isCopilotProviderID(hookInput.model.providerID))
|
|
1029
1135
|
return;
|
|
1030
1136
|
const headersBeforeOfficial = { ...output.headers };
|
|
1031
1137
|
await (await officialChatHeaders)(hookInput, output);
|
|
@@ -1125,43 +1231,49 @@ export function buildPluginHooks(input) {
|
|
|
1125
1231
|
...input.auth,
|
|
1126
1232
|
provider: authProvider,
|
|
1127
1233
|
methods: input.auth.methods,
|
|
1128
|
-
loader: enableCopilotAuthLoader ? loader : undefined,
|
|
1234
|
+
loader: enableCopilotAuthLoader ? loader : (enableCodexAuthLoader ? codexLoader : undefined),
|
|
1129
1235
|
},
|
|
1130
1236
|
config: async (config) => {
|
|
1131
1237
|
if (!config.command)
|
|
1132
1238
|
config.command = {};
|
|
1133
|
-
const store =
|
|
1239
|
+
const store = loadMergedStoreSync();
|
|
1134
1240
|
if (!areExperimentalSlashCommandsEnabled(store)) {
|
|
1135
1241
|
return;
|
|
1136
1242
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1243
|
+
if (enableCodexAuthLoader) {
|
|
1244
|
+
config.command["codex-status"] = {
|
|
1245
|
+
template: "Show the current Codex status and usage snapshot via the experimental status path.",
|
|
1246
|
+
description: "Experimental Codex status command",
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
if (enableCopilotAuthLoader) {
|
|
1250
|
+
config.command["copilot-status"] = {
|
|
1251
|
+
template: "Show the current GitHub Copilot quota status via the experimental workaround path.",
|
|
1252
|
+
description: "Experimental Copilot quota status workaround",
|
|
1253
|
+
};
|
|
1254
|
+
config.command["copilot-compact"] = {
|
|
1255
|
+
template: "Summarize the current session via real session compacting flow.",
|
|
1256
|
+
description: "Experimental compact command for Copilot sessions",
|
|
1257
|
+
};
|
|
1258
|
+
config.command["copilot-stop-tool"] = {
|
|
1259
|
+
template: "Interrupt the current session tool flow, annotate the interrupted result, and append a synthetic continue.",
|
|
1260
|
+
description: "Experimental interrupt-and-annotate recovery with synthetic continue for Copilot sessions",
|
|
1261
|
+
};
|
|
1262
|
+
config.command["copilot-inject"] = {
|
|
1263
|
+
template: "Arm an immediate tool-output inject marker flow that drives model to question.",
|
|
1264
|
+
description: "Experimental force-intervene hook for Copilot workflows",
|
|
1265
|
+
};
|
|
1266
|
+
config.command["copilot-policy-all-models"] = {
|
|
1267
|
+
template: "Toggle the current OpenCode instance policy injection scope between Copilot-only and all providers/models.",
|
|
1268
|
+
description: "Experimental policy scope toggle for all providers",
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1161
1271
|
},
|
|
1162
1272
|
"command.execute.before": async (hookInput) => {
|
|
1163
|
-
const store = await
|
|
1273
|
+
const store = await loadMergedStore();
|
|
1164
1274
|
if (hookInput.command === "copilot-inject") {
|
|
1275
|
+
if (!enableCopilotAuthLoader)
|
|
1276
|
+
return;
|
|
1165
1277
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1166
1278
|
return;
|
|
1167
1279
|
injectArmed = true;
|
|
@@ -1169,6 +1281,8 @@ export function buildPluginHooks(input) {
|
|
|
1169
1281
|
throw new InjectCommandHandledError();
|
|
1170
1282
|
}
|
|
1171
1283
|
if (hookInput.command === "copilot-policy-all-models") {
|
|
1284
|
+
if (!enableCopilotAuthLoader)
|
|
1285
|
+
return;
|
|
1172
1286
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1173
1287
|
return;
|
|
1174
1288
|
const next = getPolicyScope(store) === "all-models" ? "copilot-only" : "all-models";
|
|
@@ -1179,6 +1293,8 @@ export function buildPluginHooks(input) {
|
|
|
1179
1293
|
throw new PolicyScopeCommandHandledError();
|
|
1180
1294
|
}
|
|
1181
1295
|
if (hookInput.command === "copilot-status") {
|
|
1296
|
+
if (!enableCopilotAuthLoader)
|
|
1297
|
+
return;
|
|
1182
1298
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1183
1299
|
return;
|
|
1184
1300
|
await handleStatusCommandImpl({
|
|
@@ -1189,6 +1305,8 @@ export function buildPluginHooks(input) {
|
|
|
1189
1305
|
});
|
|
1190
1306
|
}
|
|
1191
1307
|
if (hookInput.command === "codex-status") {
|
|
1308
|
+
if (!enableCodexAuthLoader)
|
|
1309
|
+
return;
|
|
1192
1310
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1193
1311
|
return;
|
|
1194
1312
|
await handleCodexStatusCommandImpl({
|
|
@@ -1196,6 +1314,8 @@ export function buildPluginHooks(input) {
|
|
|
1196
1314
|
});
|
|
1197
1315
|
}
|
|
1198
1316
|
if (hookInput.command === "copilot-compact") {
|
|
1317
|
+
if (!enableCopilotAuthLoader)
|
|
1318
|
+
return;
|
|
1199
1319
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1200
1320
|
return;
|
|
1201
1321
|
await handleCompactCommandImpl({
|
|
@@ -1205,6 +1325,8 @@ export function buildPluginHooks(input) {
|
|
|
1205
1325
|
});
|
|
1206
1326
|
}
|
|
1207
1327
|
if (hookInput.command === "copilot-stop-tool") {
|
|
1328
|
+
if (!enableCopilotAuthLoader)
|
|
1329
|
+
return;
|
|
1208
1330
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1209
1331
|
return;
|
|
1210
1332
|
await handleStopToolCommandImpl({
|
|
@@ -1267,7 +1389,7 @@ export function buildPluginHooks(input) {
|
|
|
1267
1389
|
}
|
|
1268
1390
|
},
|
|
1269
1391
|
"chat.headers": chatHeaders,
|
|
1270
|
-
"experimental.chat.system.transform": createLoopSafetySystemTransform(
|
|
1392
|
+
"experimental.chat.system.transform": createLoopSafetySystemTransform(loadMergedStore, compactionLoopSafetyBypass.consume, lookupSessionAncestry, getPolicyScope),
|
|
1271
1393
|
"experimental.session.compacting": compactionLoopSafetyBypass.hook,
|
|
1272
1394
|
};
|
|
1273
1395
|
}
|
package/dist/plugin.js
CHANGED
|
@@ -3,8 +3,10 @@ import { listAssignableAccountsForModel, listKnownCopilotModels, rewriteModelAcc
|
|
|
3
3
|
import { runProviderMenu } from "./menu-runtime.js";
|
|
4
4
|
import { persistAccountSwitch } from "./plugin-actions.js";
|
|
5
5
|
import { buildPluginHooks } from "./plugin-hooks.js";
|
|
6
|
+
import { readCommonSettingsStore, readCommonSettingsStoreSync, writeCommonSettingsStore, } from "./common-settings-store.js";
|
|
6
7
|
import { createCodexMenuAdapter } from "./providers/codex-menu-adapter.js";
|
|
7
8
|
import { createCopilotMenuAdapter } from "./providers/copilot-menu-adapter.js";
|
|
9
|
+
import { createProviderRegistry } from "./providers/registry.js";
|
|
8
10
|
import { isTTY } from "./ui/ansi.js";
|
|
9
11
|
import { showMenu } from "./ui/menu.js";
|
|
10
12
|
import { select, selectMany } from "./ui/select.js";
|
|
@@ -12,6 +14,27 @@ import { readAuth, readStore, writeStore } from "./store.js";
|
|
|
12
14
|
function now() {
|
|
13
15
|
return Date.now();
|
|
14
16
|
}
|
|
17
|
+
function toSharedRuntimeAction(action) {
|
|
18
|
+
if (action.type === "cancel")
|
|
19
|
+
return { type: "cancel" };
|
|
20
|
+
if (action.type === "add")
|
|
21
|
+
return { type: "add" };
|
|
22
|
+
if (action.type === "remove-all")
|
|
23
|
+
return { type: "remove-all" };
|
|
24
|
+
if (action.type === "switch")
|
|
25
|
+
return { type: "switch", account: action.account };
|
|
26
|
+
if (action.type === "remove")
|
|
27
|
+
return { type: "remove", account: action.account };
|
|
28
|
+
if (action.type === "toggle-loop-safety")
|
|
29
|
+
return { type: "provider", name: "toggle-loop-safety" };
|
|
30
|
+
if (action.type === "toggle-loop-safety-provider-scope")
|
|
31
|
+
return { type: "provider", name: "toggle-loop-safety-provider-scope" };
|
|
32
|
+
if (action.type === "toggle-experimental-slash-commands")
|
|
33
|
+
return { type: "provider", name: "toggle-experimental-slash-commands" };
|
|
34
|
+
if (action.type === "toggle-network-retry")
|
|
35
|
+
return { type: "provider", name: "toggle-network-retry" };
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
15
38
|
export async function configureDefaultAccountGroup(store, selectors) {
|
|
16
39
|
const accountEntries = Object.entries(store.accounts);
|
|
17
40
|
if (accountEntries.length === 0) {
|
|
@@ -253,24 +276,28 @@ async function createAccountSwitcherPlugin(input, provider) {
|
|
|
253
276
|
logSwitchHint: () => {
|
|
254
277
|
console.log("Switched account. If a later Copilot session hits input[*].id too long after switching, enable Copilot Network Retry from the menu.");
|
|
255
278
|
},
|
|
279
|
+
readCommonSettings: readCommonSettingsStore,
|
|
280
|
+
writeCommonSettings: async (settings) => {
|
|
281
|
+
await writeCommonSettingsStore(settings);
|
|
282
|
+
},
|
|
256
283
|
});
|
|
257
284
|
const toRuntimeAction = async (accounts, store) => {
|
|
285
|
+
const common = await readCommonSettingsStore().catch(() => undefined);
|
|
258
286
|
const action = await showMenu(accounts, {
|
|
259
287
|
provider: "copilot",
|
|
260
288
|
refresh: { enabled: store.autoRefresh === true, minutes: store.refreshMinutes ?? 15 },
|
|
261
289
|
lastQuotaRefresh: store.lastQuotaRefresh,
|
|
262
290
|
modelAccountAssignmentCount: Object.keys(store.modelAccountAssignments ?? {}).length,
|
|
263
291
|
defaultAccountGroupCount: store.activeAccountNames?.length ?? (store.active ? 1 : 0),
|
|
264
|
-
loopSafetyEnabled: store.loopSafetyEnabled === true,
|
|
265
|
-
loopSafetyProviderScope: store.loopSafetyProviderScope,
|
|
266
|
-
experimentalSlashCommandsEnabled: store.experimentalSlashCommandsEnabled,
|
|
267
|
-
networkRetryEnabled: store.networkRetryEnabled === true,
|
|
292
|
+
loopSafetyEnabled: common?.loopSafetyEnabled ?? store.loopSafetyEnabled === true,
|
|
293
|
+
loopSafetyProviderScope: common?.loopSafetyProviderScope ?? store.loopSafetyProviderScope,
|
|
294
|
+
experimentalSlashCommandsEnabled: common?.experimentalSlashCommandsEnabled ?? store.experimentalSlashCommandsEnabled,
|
|
295
|
+
networkRetryEnabled: common?.networkRetryEnabled ?? store.networkRetryEnabled === true,
|
|
268
296
|
syntheticAgentInitiatorEnabled: store.syntheticAgentInitiatorEnabled === true,
|
|
269
297
|
});
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
return { type: "add" };
|
|
298
|
+
const shared = toSharedRuntimeAction(action);
|
|
299
|
+
if (shared)
|
|
300
|
+
return shared;
|
|
274
301
|
if (action.type === "import")
|
|
275
302
|
return { type: "provider", name: "import-auth" };
|
|
276
303
|
if (action.type === "refresh-identity")
|
|
@@ -287,20 +314,6 @@ async function createAccountSwitcherPlugin(input, provider) {
|
|
|
287
314
|
return { type: "provider", name: "configure-default-group" };
|
|
288
315
|
if (action.type === "assign-models")
|
|
289
316
|
return { type: "provider", name: "assign-models" };
|
|
290
|
-
if (action.type === "remove-all")
|
|
291
|
-
return { type: "remove-all" };
|
|
292
|
-
if (action.type === "switch")
|
|
293
|
-
return { type: "switch", account: action.account };
|
|
294
|
-
if (action.type === "remove")
|
|
295
|
-
return { type: "remove", account: action.account };
|
|
296
|
-
if (action.type === "toggle-loop-safety")
|
|
297
|
-
return { type: "provider", name: "toggle-loop-safety" };
|
|
298
|
-
if (action.type === "toggle-loop-safety-provider-scope")
|
|
299
|
-
return { type: "provider", name: "toggle-loop-safety-provider-scope" };
|
|
300
|
-
if (action.type === "toggle-experimental-slash-commands")
|
|
301
|
-
return { type: "provider", name: "toggle-experimental-slash-commands" };
|
|
302
|
-
if (action.type === "toggle-network-retry")
|
|
303
|
-
return { type: "provider", name: "toggle-network-retry" };
|
|
304
317
|
if (action.type === "toggle-synthetic-agent-initiator")
|
|
305
318
|
return { type: "provider", name: "toggle-synthetic-agent-initiator" };
|
|
306
319
|
return { type: "cancel" };
|
|
@@ -318,28 +331,30 @@ async function createAccountSwitcherPlugin(input, provider) {
|
|
|
318
331
|
}
|
|
319
332
|
const adapter = createCodexMenuAdapter({
|
|
320
333
|
client: codexClient,
|
|
334
|
+
readCommonSettings: readCommonSettingsStore,
|
|
335
|
+
writeCommonSettings: async (settings) => {
|
|
336
|
+
await writeCommonSettingsStore(settings);
|
|
337
|
+
},
|
|
321
338
|
});
|
|
322
339
|
const toRuntimeAction = async (accounts, store) => {
|
|
340
|
+
const common = await readCommonSettingsStore().catch(() => undefined);
|
|
323
341
|
const action = await showMenu(accounts, {
|
|
324
342
|
provider: "codex",
|
|
325
343
|
refresh: { enabled: store.autoRefresh === true, minutes: store.refreshMinutes ?? 15 },
|
|
344
|
+
loopSafetyEnabled: common?.loopSafetyEnabled ?? true,
|
|
345
|
+
loopSafetyProviderScope: common?.loopSafetyProviderScope,
|
|
346
|
+
experimentalSlashCommandsEnabled: common?.experimentalSlashCommandsEnabled,
|
|
347
|
+
networkRetryEnabled: common?.networkRetryEnabled === true,
|
|
326
348
|
});
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return { type: "add" };
|
|
349
|
+
const shared = toSharedRuntimeAction(action);
|
|
350
|
+
if (shared)
|
|
351
|
+
return shared;
|
|
331
352
|
if (action.type === "quota")
|
|
332
353
|
return { type: "provider", name: "refresh-snapshot" };
|
|
333
354
|
if (action.type === "toggle-refresh")
|
|
334
355
|
return { type: "provider", name: "toggle-refresh" };
|
|
335
356
|
if (action.type === "set-interval")
|
|
336
357
|
return { type: "provider", name: "set-interval" };
|
|
337
|
-
if (action.type === "remove-all")
|
|
338
|
-
return { type: "remove-all" };
|
|
339
|
-
if (action.type === "switch")
|
|
340
|
-
return { type: "switch", account: action.account };
|
|
341
|
-
if (action.type === "remove")
|
|
342
|
-
return { type: "remove", account: action.account };
|
|
343
358
|
return { type: "cancel" };
|
|
344
359
|
};
|
|
345
360
|
return runProviderMenu({
|
|
@@ -348,7 +363,11 @@ async function createAccountSwitcherPlugin(input, provider) {
|
|
|
348
363
|
now,
|
|
349
364
|
});
|
|
350
365
|
}
|
|
351
|
-
|
|
366
|
+
const registry = createProviderRegistry({
|
|
367
|
+
buildPluginHooks,
|
|
368
|
+
});
|
|
369
|
+
const assembled = provider === "github-copilot" ? registry.copilot.descriptor : registry.codex.descriptor;
|
|
370
|
+
return assembled.buildPluginHooks({
|
|
352
371
|
auth: {
|
|
353
372
|
provider,
|
|
354
373
|
methods: provider === "github-copilot" ? copilotMethods : codexMethods,
|
|
@@ -356,6 +375,8 @@ async function createAccountSwitcherPlugin(input, provider) {
|
|
|
356
375
|
client,
|
|
357
376
|
directory,
|
|
358
377
|
serverUrl,
|
|
378
|
+
loadCommonSettings: readCommonSettingsStore,
|
|
379
|
+
loadCommonSettingsSync: readCommonSettingsStoreSync,
|
|
359
380
|
});
|
|
360
381
|
}
|
|
361
382
|
export const CopilotAccountSwitcher = async (input) => {
|
|
@@ -3,9 +3,10 @@ import { type CodexOAuthAccount } from "../codex-oauth.js";
|
|
|
3
3
|
import { type CodexAccountEntry, type CodexStoreFile } from "../codex-store.js";
|
|
4
4
|
import type { ProviderMenuAdapter } from "../menu-runtime.js";
|
|
5
5
|
import { type AccountEntry } from "../store.js";
|
|
6
|
+
import { type CommonSettingsStore } from "../common-settings-store.js";
|
|
6
7
|
type WriteMeta = {
|
|
7
|
-
reason
|
|
8
|
-
source
|
|
8
|
+
reason?: string;
|
|
9
|
+
source?: string;
|
|
9
10
|
actionType?: string;
|
|
10
11
|
};
|
|
11
12
|
type AuthClient = {
|
|
@@ -42,6 +43,8 @@ type AdapterDependencies = {
|
|
|
42
43
|
accountId?: string;
|
|
43
44
|
}) => Promise<CodexStatusFetcherResult>;
|
|
44
45
|
runCodexOAuth?: () => Promise<CodexOAuthAccount | undefined>;
|
|
46
|
+
readCommonSettings?: () => Promise<CommonSettingsStore>;
|
|
47
|
+
writeCommonSettings?: (settings: CommonSettingsStore, meta?: WriteMeta) => Promise<void>;
|
|
45
48
|
};
|
|
46
49
|
export declare function createCodexMenuAdapter(inputDeps: AdapterDependencies): ProviderMenuAdapter<CodexStoreFile, CodexAccountEntry>;
|
|
47
50
|
export {};
|
|
@@ -5,6 +5,8 @@ import { runCodexOAuth } from "../codex-oauth.js";
|
|
|
5
5
|
import { getActiveCodexAccount, readCodexStore, writeCodexStore, } from "../codex-store.js";
|
|
6
6
|
import { recoverInvalidCodexAccount } from "../codex-invalid-account.js";
|
|
7
7
|
import { readAuth } from "../store.js";
|
|
8
|
+
import { readCommonSettingsStore, writeCommonSettingsStore, } from "../common-settings-store.js";
|
|
9
|
+
import { applyCommonSettingsAction } from "../common-settings-actions.js";
|
|
8
10
|
function pickName(input) {
|
|
9
11
|
const accountId = input.accountId?.trim();
|
|
10
12
|
if (accountId)
|
|
@@ -72,6 +74,14 @@ export function createCodexMenuAdapter(inputDeps) {
|
|
|
72
74
|
const loadAuth = inputDeps.readAuthEntries ?? readAuth;
|
|
73
75
|
const fetchStatus = inputDeps.fetchStatus ?? ((input) => fetchCodexStatus(input));
|
|
74
76
|
const authorizeOpenAIOAuth = inputDeps.runCodexOAuth ?? runCodexOAuth;
|
|
77
|
+
const readCommonSettings = inputDeps.readCommonSettings ?? readCommonSettingsStore;
|
|
78
|
+
const writeCommonSettings = async (settings, meta) => {
|
|
79
|
+
if (inputDeps.writeCommonSettings) {
|
|
80
|
+
await inputDeps.writeCommonSettings(settings, meta);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
await writeCommonSettingsStore(settings);
|
|
84
|
+
};
|
|
75
85
|
const refreshSnapshots = async (store) => {
|
|
76
86
|
const names = Object.keys(store.accounts);
|
|
77
87
|
const pendingRecoveryWarnings = new Map();
|
|
@@ -357,6 +367,17 @@ export function createCodexMenuAdapter(inputDeps) {
|
|
|
357
367
|
store.refreshMinutes = Math.max(1, Math.min(180, value));
|
|
358
368
|
return true;
|
|
359
369
|
}
|
|
370
|
+
if (action.name === "toggle-loop-safety"
|
|
371
|
+
|| action.name === "toggle-loop-safety-provider-scope"
|
|
372
|
+
|| action.name === "toggle-experimental-slash-commands"
|
|
373
|
+
|| action.name === "toggle-network-retry") {
|
|
374
|
+
await applyCommonSettingsAction({
|
|
375
|
+
action: { type: action.name },
|
|
376
|
+
readSettings: readCommonSettings,
|
|
377
|
+
writeSettings: writeCommonSettings,
|
|
378
|
+
});
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
360
381
|
return false;
|
|
361
382
|
},
|
|
362
383
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ProviderMenuAdapter } from "../menu-runtime.js";
|
|
2
|
+
import { type CommonSettingsStore } from "../common-settings-store.js";
|
|
2
3
|
import { type AccountEntry, type StoreFile, type StoreWriteDebugMeta } from "../store.js";
|
|
3
4
|
type AuthClient = {
|
|
4
5
|
auth: {
|
|
@@ -60,6 +61,8 @@ type AdapterDependencies = {
|
|
|
60
61
|
now?: () => number;
|
|
61
62
|
}) => Promise<void>;
|
|
62
63
|
logSwitchHint?: () => void;
|
|
64
|
+
readCommonSettings?: () => Promise<CommonSettingsStore>;
|
|
65
|
+
writeCommonSettings?: (settings: CommonSettingsStore, meta?: DebugMeta) => Promise<void>;
|
|
63
66
|
};
|
|
64
67
|
export declare function createCopilotMenuAdapter(inputDeps: AdapterDependencies): ProviderMenuAdapter<StoreFile, AccountEntry>;
|
|
65
68
|
export {};
|
|
@@ -4,6 +4,7 @@ import { fetchQuota } from "../active-account-quota.js";
|
|
|
4
4
|
import { getGitHubToken, normalizeDomain } from "../copilot-api-helpers.js";
|
|
5
5
|
import { listAssignableAccountsForModel, listKnownCopilotModels, rewriteModelAccountAssignments, } from "../model-account-map.js";
|
|
6
6
|
import { applyMenuAction, persistAccountSwitch } from "../plugin-actions.js";
|
|
7
|
+
import { readCommonSettingsStore, writeCommonSettingsStore, } from "../common-settings-store.js";
|
|
7
8
|
import { select, selectMany } from "../ui/select.js";
|
|
8
9
|
import { authPath, readAuth, readStore } from "../store.js";
|
|
9
10
|
const CLIENT_ID = "Ov23li8tweQw6odWQebz";
|
|
@@ -387,6 +388,8 @@ export function createCopilotMenuAdapter(inputDeps) {
|
|
|
387
388
|
const fetchUserFn = inputDeps.fetchUser ?? fetchUserDefault;
|
|
388
389
|
const fetchModelsFn = inputDeps.fetchModels ?? fetchModelsDefault;
|
|
389
390
|
const fetchQuotaFn = inputDeps.fetchQuota ?? fetchQuota;
|
|
391
|
+
const readCommonSettings = inputDeps.readCommonSettings ?? readCommonSettingsStore;
|
|
392
|
+
const writeCommonSettings = inputDeps.writeCommonSettings ?? writeCommonSettingsStore;
|
|
390
393
|
let nextAutoRefreshAt = 0;
|
|
391
394
|
async function maybeAutoRefresh(store) {
|
|
392
395
|
if (store.autoRefresh !== true || now() < nextAutoRefreshAt)
|
|
@@ -728,6 +731,8 @@ export function createCopilotMenuAdapter(inputDeps) {
|
|
|
728
731
|
action: { type: action.name },
|
|
729
732
|
store,
|
|
730
733
|
writeStore: persistStore,
|
|
734
|
+
readCommonSettings,
|
|
735
|
+
writeCommonSettings,
|
|
731
736
|
});
|
|
732
737
|
return false;
|
|
733
738
|
}
|
|
@@ -22,7 +22,8 @@ export declare const CODEX_PROVIDER_DESCRIPTOR: ProviderDescriptor;
|
|
|
22
22
|
export declare function createCopilotProviderDescriptor(input: {
|
|
23
23
|
buildPluginHooks: BuildPluginHooks;
|
|
24
24
|
}): AssembledProviderDescriptor;
|
|
25
|
-
export declare function createCodexProviderDescriptor(input
|
|
25
|
+
export declare function createCodexProviderDescriptor(input: {
|
|
26
|
+
buildPluginHooks: BuildPluginHooks;
|
|
26
27
|
enabled?: boolean;
|
|
27
|
-
}):
|
|
28
|
+
}): AssembledProviderDescriptor;
|
|
28
29
|
export {};
|
|
@@ -47,6 +47,8 @@ export const CODEX_PROVIDER_DESCRIPTOR = {
|
|
|
47
47
|
],
|
|
48
48
|
capabilities: [
|
|
49
49
|
"auth",
|
|
50
|
+
"chat-headers",
|
|
51
|
+
"network-retry",
|
|
50
52
|
"slash-commands",
|
|
51
53
|
],
|
|
52
54
|
};
|
|
@@ -60,12 +62,13 @@ export function createCopilotProviderDescriptor(input) {
|
|
|
60
62
|
enabledByDefault: true,
|
|
61
63
|
};
|
|
62
64
|
}
|
|
63
|
-
export function createCodexProviderDescriptor(input
|
|
65
|
+
export function createCodexProviderDescriptor(input) {
|
|
64
66
|
return {
|
|
65
67
|
key: "codex",
|
|
66
68
|
auth: {
|
|
67
69
|
provider: "openai",
|
|
68
70
|
},
|
|
71
|
+
buildPluginHooks: input.buildPluginHooks,
|
|
69
72
|
enabledByDefault: input.enabled !== false,
|
|
70
73
|
};
|
|
71
74
|
}
|
|
@@ -12,7 +12,7 @@ export declare function createProviderRegistry(input: {
|
|
|
12
12
|
descriptor: import("./descriptor.js").AssembledProviderDescriptor;
|
|
13
13
|
};
|
|
14
14
|
codex: {
|
|
15
|
-
descriptor:
|
|
15
|
+
descriptor: import("./descriptor.js").AssembledProviderDescriptor;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
export {};
|