opencode-copilot-account-switcher 0.13.3 → 0.13.5
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-status-fetcher.d.ts +1 -0
- package/dist/codex-status-fetcher.js +10 -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 +125 -0
- package/dist/menu-runtime.d.ts +1 -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 +167 -43
- package/dist/plugin.js +54 -33
- package/dist/providers/codex-menu-adapter.d.ts +5 -2
- package/dist/providers/codex-menu-adapter.js +23 -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 +3 -0
- package/dist/ui/menu.js +57 -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")
|
|
@@ -421,9 +446,17 @@ function pruneTouchWriteCache(input) {
|
|
|
421
446
|
}
|
|
422
447
|
}
|
|
423
448
|
export function buildPluginHooks(input) {
|
|
449
|
+
const authProvider = input.auth.provider ?? COPILOT_PROVIDER_DESCRIPTOR.providerIDs[0] ?? "github-copilot";
|
|
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;
|
|
424
455
|
const compactionLoopSafetyBypass = createCompactionLoopSafetyBypass();
|
|
425
456
|
const loadStore = input.loadStore ?? readStoreSafe;
|
|
426
457
|
const loadStoreSync = input.loadStoreSync ?? readStoreSafeSync;
|
|
458
|
+
const loadCommonSettings = input.loadCommonSettings;
|
|
459
|
+
const loadCommonSettingsSync = input.loadCommonSettingsSync;
|
|
427
460
|
const persistStore = (store, meta) => {
|
|
428
461
|
if (input.writeStore)
|
|
429
462
|
return input.writeStore(store, meta);
|
|
@@ -434,9 +467,30 @@ export function buildPluginHooks(input) {
|
|
|
434
467
|
const handleCodexStatusCommandImpl = input.handleCodexStatusCommandImpl ?? handleCodexStatusCommand;
|
|
435
468
|
const handleCompactCommandImpl = input.handleCompactCommandImpl ?? handleCompactCommand;
|
|
436
469
|
const handleStopToolCommandImpl = input.handleStopToolCommandImpl ?? handleStopToolCommand;
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
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);
|
|
440
494
|
const now = input.now ?? (() => Date.now());
|
|
441
495
|
const random = input.random ?? Math.random;
|
|
442
496
|
let injectArmed = false;
|
|
@@ -462,6 +516,27 @@ export function buildPluginHooks(input) {
|
|
|
462
516
|
});
|
|
463
517
|
});
|
|
464
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
|
+
};
|
|
465
540
|
const showInjectToast = async (message, variant = "info") => {
|
|
466
541
|
await showStatusToast({
|
|
467
542
|
client: input.client,
|
|
@@ -566,6 +641,7 @@ export function buildPluginHooks(input) {
|
|
|
566
641
|
const finalHeaderCapture = new AsyncLocalStorage();
|
|
567
642
|
const getScopedAuth = async () => authOverride.getStore() ?? getAuth();
|
|
568
643
|
const providerConfig = provider;
|
|
644
|
+
const loadOfficialConfig = enableCodexAuthLoader ? loadOfficialConfigForCodex : loadOfficialConfigForCopilot;
|
|
569
645
|
const config = await loadOfficialConfig({
|
|
570
646
|
getAuth: getScopedAuth,
|
|
571
647
|
provider: providerConfig,
|
|
@@ -995,35 +1071,67 @@ export function buildPluginHooks(input) {
|
|
|
995
1071
|
}
|
|
996
1072
|
return response;
|
|
997
1073
|
};
|
|
998
|
-
|
|
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)
|
|
999
1082
|
return {
|
|
1000
1083
|
...config,
|
|
1001
|
-
fetch:
|
|
1084
|
+
fetch: providerFetch,
|
|
1002
1085
|
};
|
|
1003
1086
|
return {
|
|
1004
1087
|
...config,
|
|
1005
|
-
fetch: createRetryFetch(
|
|
1088
|
+
fetch: createRetryFetch(providerFetch, {
|
|
1006
1089
|
client: input.client,
|
|
1007
1090
|
directory: input.directory,
|
|
1008
1091
|
serverUrl: input.serverUrl,
|
|
1009
|
-
lastAccountSwitchAt: retryStore
|
|
1092
|
+
lastAccountSwitchAt: retryStore?.lastAccountSwitchAt,
|
|
1010
1093
|
notifier: createCopilotRetryNotifier({
|
|
1011
1094
|
client: input.client,
|
|
1012
|
-
lastAccountSwitchAt: retryStore
|
|
1095
|
+
lastAccountSwitchAt: retryStore?.lastAccountSwitchAt,
|
|
1013
1096
|
getLastAccountSwitchAt: getLatestLastAccountSwitchAt,
|
|
1014
1097
|
clearAccountSwitchContext,
|
|
1015
1098
|
now: input.now,
|
|
1016
1099
|
}),
|
|
1017
|
-
clearAccountSwitchContext: async () => clearAccountSwitchContext(retryStore
|
|
1100
|
+
clearAccountSwitchContext: async () => clearAccountSwitchContext(retryStore?.lastAccountSwitchAt),
|
|
1018
1101
|
}),
|
|
1019
1102
|
};
|
|
1020
1103
|
};
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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 () => { });
|
|
1025
1127
|
const chatHeaders = async (hookInput, output) => {
|
|
1026
|
-
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))
|
|
1027
1135
|
return;
|
|
1028
1136
|
const headersBeforeOfficial = { ...output.headers };
|
|
1029
1137
|
await (await officialChatHeaders)(hookInput, output);
|
|
@@ -1121,45 +1229,51 @@ export function buildPluginHooks(input) {
|
|
|
1121
1229
|
return {
|
|
1122
1230
|
auth: {
|
|
1123
1231
|
...input.auth,
|
|
1124
|
-
provider:
|
|
1232
|
+
provider: authProvider,
|
|
1125
1233
|
methods: input.auth.methods,
|
|
1126
|
-
loader,
|
|
1234
|
+
loader: enableCopilotAuthLoader ? loader : (enableCodexAuthLoader ? codexLoader : undefined),
|
|
1127
1235
|
},
|
|
1128
1236
|
config: async (config) => {
|
|
1129
1237
|
if (!config.command)
|
|
1130
1238
|
config.command = {};
|
|
1131
|
-
const store =
|
|
1239
|
+
const store = loadMergedStoreSync();
|
|
1132
1240
|
if (!areExperimentalSlashCommandsEnabled(store)) {
|
|
1133
1241
|
return;
|
|
1134
1242
|
}
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
+
}
|
|
1159
1271
|
},
|
|
1160
1272
|
"command.execute.before": async (hookInput) => {
|
|
1161
|
-
const store = await
|
|
1273
|
+
const store = await loadMergedStore();
|
|
1162
1274
|
if (hookInput.command === "copilot-inject") {
|
|
1275
|
+
if (!enableCopilotAuthLoader)
|
|
1276
|
+
return;
|
|
1163
1277
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1164
1278
|
return;
|
|
1165
1279
|
injectArmed = true;
|
|
@@ -1167,6 +1281,8 @@ export function buildPluginHooks(input) {
|
|
|
1167
1281
|
throw new InjectCommandHandledError();
|
|
1168
1282
|
}
|
|
1169
1283
|
if (hookInput.command === "copilot-policy-all-models") {
|
|
1284
|
+
if (!enableCopilotAuthLoader)
|
|
1285
|
+
return;
|
|
1170
1286
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1171
1287
|
return;
|
|
1172
1288
|
const next = getPolicyScope(store) === "all-models" ? "copilot-only" : "all-models";
|
|
@@ -1177,6 +1293,8 @@ export function buildPluginHooks(input) {
|
|
|
1177
1293
|
throw new PolicyScopeCommandHandledError();
|
|
1178
1294
|
}
|
|
1179
1295
|
if (hookInput.command === "copilot-status") {
|
|
1296
|
+
if (!enableCopilotAuthLoader)
|
|
1297
|
+
return;
|
|
1180
1298
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1181
1299
|
return;
|
|
1182
1300
|
await handleStatusCommandImpl({
|
|
@@ -1187,6 +1305,8 @@ export function buildPluginHooks(input) {
|
|
|
1187
1305
|
});
|
|
1188
1306
|
}
|
|
1189
1307
|
if (hookInput.command === "codex-status") {
|
|
1308
|
+
if (!enableCodexAuthLoader)
|
|
1309
|
+
return;
|
|
1190
1310
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1191
1311
|
return;
|
|
1192
1312
|
await handleCodexStatusCommandImpl({
|
|
@@ -1194,6 +1314,8 @@ export function buildPluginHooks(input) {
|
|
|
1194
1314
|
});
|
|
1195
1315
|
}
|
|
1196
1316
|
if (hookInput.command === "copilot-compact") {
|
|
1317
|
+
if (!enableCopilotAuthLoader)
|
|
1318
|
+
return;
|
|
1197
1319
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1198
1320
|
return;
|
|
1199
1321
|
await handleCompactCommandImpl({
|
|
@@ -1203,6 +1325,8 @@ export function buildPluginHooks(input) {
|
|
|
1203
1325
|
});
|
|
1204
1326
|
}
|
|
1205
1327
|
if (hookInput.command === "copilot-stop-tool") {
|
|
1328
|
+
if (!enableCopilotAuthLoader)
|
|
1329
|
+
return;
|
|
1206
1330
|
if (!areExperimentalSlashCommandsEnabled(store))
|
|
1207
1331
|
return;
|
|
1208
1332
|
await handleStopToolCommandImpl({
|
|
@@ -1265,7 +1389,7 @@ export function buildPluginHooks(input) {
|
|
|
1265
1389
|
}
|
|
1266
1390
|
},
|
|
1267
1391
|
"chat.headers": chatHeaders,
|
|
1268
|
-
"experimental.chat.system.transform": createLoopSafetySystemTransform(
|
|
1392
|
+
"experimental.chat.system.transform": createLoopSafetySystemTransform(loadMergedStore, compactionLoopSafetyBypass.consume, lookupSessionAncestry, getPolicyScope),
|
|
1269
1393
|
"experimental.session.compacting": compactionLoopSafetyBypass.hook,
|
|
1270
1394
|
};
|
|
1271
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();
|
|
@@ -255,6 +265,7 @@ export function createCodexMenuAdapter(inputDeps) {
|
|
|
255
265
|
fallback: `openai-${now()}`,
|
|
256
266
|
}),
|
|
257
267
|
providerId: "openai",
|
|
268
|
+
workspaceName: oauth.workspaceName,
|
|
258
269
|
refresh,
|
|
259
270
|
access,
|
|
260
271
|
expires: oauth.expires,
|
|
@@ -269,6 +280,7 @@ export function createCodexMenuAdapter(inputDeps) {
|
|
|
269
280
|
return Object.entries(store.accounts).map(([name, entry], index) => ({
|
|
270
281
|
id: entry.accountId ?? name,
|
|
271
282
|
name: entry.email ?? entry.accountId ?? name,
|
|
283
|
+
workspaceName: entry.workspaceName,
|
|
272
284
|
index,
|
|
273
285
|
isCurrent: store.active === name,
|
|
274
286
|
source: entry.source,
|
|
@@ -355,6 +367,17 @@ export function createCodexMenuAdapter(inputDeps) {
|
|
|
355
367
|
store.refreshMinutes = Math.max(1, Math.min(180, value));
|
|
356
368
|
return true;
|
|
357
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
|
+
}
|
|
358
381
|
return false;
|
|
359
382
|
},
|
|
360
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 {};
|