opencode-copilot-account-switcher 0.14.26 → 0.14.28

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/plugin.js CHANGED
@@ -10,6 +10,7 @@ import { brokerStartupDiagnosticsPath, ensureWechatStateLayout } from "./wechat/
10
10
  import { createCodexMenuAdapter } from "./providers/codex-menu-adapter.js";
11
11
  import { createCopilotMenuAdapter } from "./providers/copilot-menu-adapter.js";
12
12
  import { createProviderRegistry } from "./providers/registry.js";
13
+ import { loadOfficialCodexAuthMethods } from "./upstream/codex-loader-adapter.js";
13
14
  import { isTTY } from "./ui/ansi.js";
14
15
  import { showMenu } from "./ui/menu.js";
15
16
  import { select, selectMany } from "./ui/select.js";
@@ -73,6 +74,14 @@ function toSharedRuntimeAction(action) {
73
74
  return { type: "provider", name: "toggle-experimental-slash-commands" };
74
75
  if (action.type === "toggle-network-retry")
75
76
  return { type: "provider", name: "toggle-network-retry" };
77
+ if (action.type === "toggle-wechat-notifications")
78
+ return { type: "provider", name: "toggle-wechat-notifications" };
79
+ if (action.type === "toggle-wechat-question-notify")
80
+ return { type: "provider", name: "toggle-wechat-question-notify" };
81
+ if (action.type === "toggle-wechat-permission-notify")
82
+ return { type: "provider", name: "toggle-wechat-permission-notify" };
83
+ if (action.type === "toggle-wechat-session-error-notify")
84
+ return { type: "provider", name: "toggle-wechat-session-error-notify" };
76
85
  if (action.type === "wechat-bind")
77
86
  return { type: "provider", name: "wechat-bind" };
78
87
  if (action.type === "wechat-rebind")
@@ -386,6 +395,13 @@ async function createAccountSwitcherPlugin(input, provider) {
386
395
  }
387
396
  const adapter = createCodexMenuAdapter({
388
397
  client: codexClient,
398
+ loadOfficialCodexAuthMethods: () => loadOfficialCodexAuthMethods({
399
+ client: {
400
+ auth: {
401
+ set: async (value) => client.auth.set(value),
402
+ },
403
+ },
404
+ }),
389
405
  readCommonSettings: readCommonSettingsStore,
390
406
  writeCommonSettings: async (settings) => {
391
407
  await writeCommonSettingsStore(settings);
@@ -1,5 +1,5 @@
1
1
  import { type CodexStatusFetcherResult } from "../codex-status-fetcher.js";
2
- import { type CodexOAuthAccount } from "../codex-oauth.js";
2
+ import { type OfficialCodexAuthMethod } from "../upstream/codex-loader-adapter.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";
@@ -42,7 +42,7 @@ type AdapterDependencies = {
42
42
  };
43
43
  accountId?: string;
44
44
  }) => Promise<CodexStatusFetcherResult>;
45
- runCodexOAuth?: () => Promise<CodexOAuthAccount | undefined>;
45
+ loadOfficialCodexAuthMethods?: () => Promise<OfficialCodexAuthMethod[]>;
46
46
  readCommonSettings?: () => Promise<CommonSettingsStore>;
47
47
  writeCommonSettings?: (settings: CommonSettingsStore, meta?: WriteMeta) => Promise<void>;
48
48
  };
@@ -1,13 +1,34 @@
1
1
  import { createInterface } from "node:readline/promises";
2
2
  import { stdin as input, stdout as output } from "node:process";
3
3
  import { fetchCodexStatus } from "../codex-status-fetcher.js";
4
- import { runCodexOAuth } from "../codex-oauth.js";
4
+ import { loadOfficialCodexAuthMethods, } from "../upstream/codex-loader-adapter.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
8
  import { readCommonSettingsStore, writeCommonSettingsStore, } from "../common-settings-store.js";
9
9
  import { applyCommonSettingsAction } from "../common-settings-actions.js";
10
- import { runWechatBindFlow } from "../wechat/bind-flow.js";
10
+ function pickOfficialOauthMethodByKind(methods, kind) {
11
+ return methods.find((method) => {
12
+ if (method.type !== "oauth")
13
+ return false;
14
+ if (typeof method.authorize !== "function")
15
+ return false;
16
+ const label = method.label.toLowerCase();
17
+ if (kind === "browser")
18
+ return label.includes("browser");
19
+ return label.includes("headless") || label.includes("device");
20
+ });
21
+ }
22
+ function parseOfficialOauthSelection(raw) {
23
+ const value = raw.trim().toLowerCase();
24
+ if (!value)
25
+ return "cancel";
26
+ if (value === "1" || value === "browser" || value === "b")
27
+ return "browser";
28
+ if (value === "2" || value === "headless" || value === "h" || value === "device")
29
+ return "headless";
30
+ return undefined;
31
+ }
11
32
  function pickName(input) {
12
33
  const accountId = input.accountId?.trim();
13
34
  if (accountId)
@@ -74,7 +95,14 @@ export function createCodexMenuAdapter(inputDeps) {
74
95
  });
75
96
  const loadAuth = inputDeps.readAuthEntries ?? readAuth;
76
97
  const fetchStatus = inputDeps.fetchStatus ?? ((input) => fetchCodexStatus(input));
77
- const authorizeOpenAIOAuth = inputDeps.runCodexOAuth ?? runCodexOAuth;
98
+ const loadOfficialMethods = inputDeps.loadOfficialCodexAuthMethods
99
+ ?? (() => loadOfficialCodexAuthMethods({
100
+ client: {
101
+ auth: {
102
+ set: async (value) => inputDeps.client.auth.set(value),
103
+ },
104
+ },
105
+ }));
78
106
  const readCommonSettings = inputDeps.readCommonSettings ?? readCommonSettingsStore;
79
107
  const writeCommonSettings = async (settings, meta) => {
80
108
  if (inputDeps.writeCommonSettings) {
@@ -244,34 +272,46 @@ export function createCodexMenuAdapter(inputDeps) {
244
272
  return true;
245
273
  },
246
274
  authorizeNewAccount: async () => {
247
- const oauth = await authorizeOpenAIOAuth();
248
- if (!oauth || (!oauth.refresh && !oauth.access))
275
+ const methods = await loadOfficialMethods();
276
+ const browserMethod = pickOfficialOauthMethodByKind(methods, "browser");
277
+ const headlessMethod = pickOfficialOauthMethodByKind(methods, "headless");
278
+ const selectedKey = parseOfficialOauthSelection(await prompt("Choose Codex auth method (1/browser/b, 2/headless/h/device, Enter to cancel): "));
279
+ if (selectedKey === "cancel" || !selectedKey)
280
+ return undefined;
281
+ const selectedMethod = selectedKey === "browser" ? browserMethod : headlessMethod;
282
+ if (!selectedMethod || typeof selectedMethod.authorize !== "function")
283
+ return undefined;
284
+ const pending = await selectedMethod.authorize();
285
+ if (pending.method && pending.method !== "auto") {
286
+ throw new Error(`Unsupported official Codex auth method: ${pending.method}`);
287
+ }
288
+ if (typeof pending.callback !== "function")
289
+ return undefined;
290
+ const result = await pending.callback();
291
+ if (result.type !== "success" || (!result.refresh && !result.access))
249
292
  return undefined;
250
- const refresh = oauth.refresh ?? oauth.access;
251
- const access = oauth.access ?? oauth.refresh;
293
+ const refresh = result.refresh ?? result.access;
294
+ const access = result.access ?? result.refresh;
252
295
  await inputDeps.client.auth.set({
253
296
  path: { id: "openai" },
254
297
  body: {
255
298
  type: "oauth",
256
299
  refresh,
257
300
  access,
258
- expires: oauth.expires,
259
- accountId: oauth.accountId,
301
+ expires: result.expires,
302
+ accountId: result.accountId,
260
303
  },
261
304
  });
262
305
  return {
263
306
  name: pickName({
264
- accountId: oauth.accountId,
265
- email: oauth.email,
307
+ accountId: result.accountId,
266
308
  fallback: `openai-${now()}`,
267
309
  }),
268
310
  providerId: "openai",
269
- workspaceName: oauth.workspaceName,
270
311
  refresh,
271
312
  access,
272
- expires: oauth.expires,
273
- accountId: oauth.accountId,
274
- email: oauth.email,
313
+ expires: result.expires,
314
+ accountId: result.accountId,
275
315
  source: "manual",
276
316
  addedAt: now(),
277
317
  };
@@ -384,6 +424,7 @@ export function createCodexMenuAdapter(inputDeps) {
384
424
  return false;
385
425
  }
386
426
  if (action.name === "wechat-bind" || action.name === "wechat-rebind") {
427
+ const { runWechatBindFlow } = await import("../wechat/bind-flow.js");
387
428
  await runWechatBindFlow({
388
429
  action: action.name,
389
430
  readCommonSettings,
@@ -5,7 +5,6 @@ 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
7
  import { readCommonSettingsStore, writeCommonSettingsStore, } from "../common-settings-store.js";
8
- import { runWechatBindFlow } from "../wechat/bind-flow.js";
9
8
  import { select, selectMany } from "../ui/select.js";
10
9
  import { authPath, readAuth, readStore } from "../store.js";
11
10
  const CLIENT_ID = "Ov23li8tweQw6odWQebz";
@@ -742,6 +741,7 @@ export function createCopilotMenuAdapter(inputDeps) {
742
741
  return false;
743
742
  }
744
743
  if (action.name === "wechat-bind" || action.name === "wechat-rebind") {
744
+ const { runWechatBindFlow } = await import("../wechat/bind-flow.js");
745
745
  await runWechatBindFlow({
746
746
  action: action.name,
747
747
  readCommonSettings,
@@ -36,6 +36,35 @@ export type OfficialCodexChatHeadersHook = (input: {
36
36
  }, output: {
37
37
  headers: Record<string, string>;
38
38
  }) => Promise<void>;
39
+ type OfficialCodexAuthResult = {
40
+ type: "success";
41
+ refresh: string;
42
+ access: string;
43
+ expires: number;
44
+ accountId?: string;
45
+ } | {
46
+ type: "failed";
47
+ };
48
+ type OfficialCodexAuthorizePending = {
49
+ url: string;
50
+ instructions?: string;
51
+ method?: string;
52
+ callback?: () => Promise<OfficialCodexAuthResult>;
53
+ };
54
+ export type OfficialCodexAuthMethod = {
55
+ label: string;
56
+ type: string;
57
+ authorize?: () => Promise<OfficialCodexAuthorizePending>;
58
+ };
59
+ export declare function loadOfficialCodexAuthMethods(input?: {
60
+ client?: {
61
+ auth?: {
62
+ set?: (value: unknown) => Promise<unknown>;
63
+ };
64
+ };
65
+ baseFetch?: typeof fetch;
66
+ version?: string;
67
+ }): Promise<OfficialCodexAuthMethod[]>;
39
68
  export declare function loadOfficialCodexConfig(input: {
40
69
  getAuth: () => Promise<CodexAuthState | undefined>;
41
70
  provider?: CodexProviderConfig;
@@ -67,3 +96,4 @@ export declare function loadOfficialCodexChatHeaders(input?: {
67
96
  baseFetch?: typeof fetch;
68
97
  version?: string;
69
98
  }): Promise<OfficialCodexChatHeadersHook>;
99
+ export {};
@@ -13,6 +13,31 @@ async function loadOfficialHooks(input) {
13
13
  return hooks;
14
14
  });
15
15
  }
16
+ export async function loadOfficialCodexAuthMethods(input = {}) {
17
+ const hooks = await loadOfficialHooks(input);
18
+ const methods = hooks.auth?.methods;
19
+ if (!Array.isArray(methods)) {
20
+ return [];
21
+ }
22
+ return methods.map((method) => {
23
+ if (typeof method.authorize !== "function") {
24
+ return method;
25
+ }
26
+ return {
27
+ ...method,
28
+ authorize: async () => {
29
+ const pending = await runWithOfficialBridge(input, () => method.authorize());
30
+ if (!pending || typeof pending.callback !== "function") {
31
+ return pending;
32
+ }
33
+ return {
34
+ ...pending,
35
+ callback: () => runWithOfficialBridge(input, () => pending.callback()),
36
+ };
37
+ },
38
+ };
39
+ });
40
+ }
16
41
  export async function loadOfficialCodexConfig(input) {
17
42
  const hooks = await loadOfficialHooks({
18
43
  client: input.client,
@@ -9,5 +9,24 @@ declare const officialCodexExportBridge: {
9
9
  version?: string;
10
10
  } | undefined, fn: () => Promise<any>): Promise<any>;
11
11
  };
12
+ export interface IdTokenClaims {
13
+ chatgpt_account_id?: string;
14
+ organizations?: Array<{
15
+ id: string;
16
+ }>;
17
+ email?: string;
18
+ "https://api.openai.com/auth"?: {
19
+ chatgpt_account_id?: string;
20
+ };
21
+ }
22
+ export declare function parseJwtClaims(token: string): IdTokenClaims | undefined;
23
+ export declare function extractAccountIdFromClaims(claims: IdTokenClaims): string | undefined;
24
+ export declare function extractAccountId(tokens: TokenResponse): string | undefined;
25
+ interface TokenResponse {
26
+ id_token: string;
27
+ access_token: string;
28
+ refresh_token: string;
29
+ expires_in?: number;
30
+ }
12
31
  export declare function CodexAuthPlugin(input: PluginInput): Promise<Hooks>;
13
32
  export { officialCodexExportBridge };