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.
Files changed (39) hide show
  1. package/dist/codex-network-retry.d.ts +2 -0
  2. package/dist/codex-network-retry.js +9 -0
  3. package/dist/codex-status-fetcher.d.ts +1 -0
  4. package/dist/codex-status-fetcher.js +10 -0
  5. package/dist/codex-store.js +11 -8
  6. package/dist/common-settings-actions.d.ts +15 -0
  7. package/dist/common-settings-actions.js +42 -0
  8. package/dist/common-settings-store.d.ts +20 -0
  9. package/dist/common-settings-store.js +125 -0
  10. package/dist/menu-runtime.d.ts +1 -0
  11. package/dist/plugin-actions.d.ts +9 -0
  12. package/dist/plugin-actions.js +15 -35
  13. package/dist/plugin-hooks.d.ts +5 -0
  14. package/dist/plugin-hooks.js +167 -43
  15. package/dist/plugin.js +54 -33
  16. package/dist/providers/codex-menu-adapter.d.ts +5 -2
  17. package/dist/providers/codex-menu-adapter.js +23 -0
  18. package/dist/providers/copilot-menu-adapter.d.ts +3 -0
  19. package/dist/providers/copilot-menu-adapter.js +5 -0
  20. package/dist/providers/descriptor.d.ts +3 -2
  21. package/dist/providers/descriptor.js +4 -1
  22. package/dist/providers/registry.d.ts +1 -1
  23. package/dist/providers/registry.js +38 -2
  24. package/dist/retry/codex-policy.d.ts +5 -0
  25. package/dist/retry/codex-policy.js +75 -0
  26. package/dist/retry/common-policy.d.ts +37 -0
  27. package/dist/retry/common-policy.js +68 -0
  28. package/dist/retry/copilot-policy.d.ts +1 -10
  29. package/dist/retry/copilot-policy.js +24 -58
  30. package/dist/store-paths.d.ts +6 -0
  31. package/dist/store-paths.js +24 -0
  32. package/dist/store.js +34 -8
  33. package/dist/ui/menu.d.ts +3 -0
  34. package/dist/ui/menu.js +57 -46
  35. package/dist/upstream/codex-loader-adapter.d.ts +69 -0
  36. package/dist/upstream/codex-loader-adapter.js +55 -0
  37. package/dist/upstream/codex-plugin.snapshot.d.ts +13 -0
  38. package/dist/upstream/codex-plugin.snapshot.js +98 -0
  39. package/package.json +3 -1
package/dist/ui/menu.js CHANGED
@@ -10,10 +10,10 @@ function defaultMenuCapabilities(provider) {
10
10
  checkModels: false,
11
11
  defaultAccountGroup: false,
12
12
  assignModels: false,
13
- loopSafety: false,
14
- policyScope: false,
15
- experimentalSlashCommands: false,
16
- networkRetry: false,
13
+ loopSafety: true,
14
+ policyScope: true,
15
+ experimentalSlashCommands: true,
16
+ networkRetry: true,
17
17
  syntheticAgentInitiator: false,
18
18
  };
19
19
  }
@@ -39,6 +39,8 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
39
39
  menuSubtitle: "Select an action or account",
40
40
  switchLanguageLabel: "切换到中文",
41
41
  actionsHeading: "Actions",
42
+ commonSettingsHeading: "Common settings",
43
+ providerSettingsHeading: "Provider settings",
42
44
  addAccount: "Add account",
43
45
  addAccountHint: "OpenAI OAuth login",
44
46
  importAuth: "Import from auth.json",
@@ -75,6 +77,8 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
75
77
  menuSubtitle: "请选择操作或账号",
76
78
  switchLanguageLabel: "Switch to English",
77
79
  actionsHeading: "操作",
80
+ commonSettingsHeading: "通用设置",
81
+ providerSettingsHeading: "Provider 专属设置",
78
82
  addAccount: "添加账号",
79
83
  addAccountHint: "OpenAI OAuth 登录",
80
84
  importAuth: "从 auth.json 导入",
@@ -112,6 +116,8 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
112
116
  menuSubtitle: "Select an action or account",
113
117
  switchLanguageLabel: "切换到中文",
114
118
  actionsHeading: "Actions",
119
+ commonSettingsHeading: "Common settings",
120
+ providerSettingsHeading: "Provider settings",
115
121
  addAccount: "Add account",
116
122
  addAccountHint: "device login or manual",
117
123
  importAuth: "Import from auth.json",
@@ -132,9 +138,9 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
132
138
  experimentalSlashCommandsOn: "Experimental slash commands: On",
133
139
  experimentalSlashCommandsOff: "Experimental slash commands: Off",
134
140
  experimentalSlashCommandsHint: "Controls whether /copilot-status, /copilot-compact, /copilot-stop-tool, /copilot-inject, and /copilot-policy-all-models are registered",
135
- retryOn: "Copilot Network Retry: On",
136
- retryOff: "Copilot Network Retry: Off",
137
- retryHint: "Helps recover some requests after account switches or malformed retries",
141
+ retryOn: "Network Retry: On",
142
+ retryOff: "Network Retry: Off",
143
+ retryHint: "Helps recover some requests after retries or malformed responses",
138
144
  syntheticInitiatorOn: "Send synthetic messages as agent: On",
139
145
  syntheticInitiatorOff: "Send synthetic messages as agent: Off",
140
146
  syntheticInitiatorHint: "Changes upstream behavior; misuse may increase billing risk or trigger abuse signals",
@@ -148,6 +154,8 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
148
154
  menuSubtitle: "请选择操作或账号",
149
155
  switchLanguageLabel: "Switch to English",
150
156
  actionsHeading: "操作",
157
+ commonSettingsHeading: "通用设置",
158
+ providerSettingsHeading: "Provider 专属设置",
151
159
  addAccount: "添加账号",
152
160
  addAccountHint: "设备登录或手动录入",
153
161
  importAuth: "从 auth.json 导入",
@@ -168,9 +176,9 @@ export function getMenuCopy(language = "zh", provider = "copilot") {
168
176
  experimentalSlashCommandsOn: "实验性 Slash Commands:已开启",
169
177
  experimentalSlashCommandsOff: "实验性 Slash Commands:已关闭",
170
178
  experimentalSlashCommandsHint: "控制 /copilot-status、/copilot-compact、/copilot-stop-tool、/copilot-inject、/copilot-policy-all-models 是否注册",
171
- retryOn: "Copilot Network Retry:已开启",
172
- retryOff: "Copilot Network Retry:已关闭",
173
- retryHint: "账号切换后若出现请求异常,可自动重试并修复部分请求",
179
+ retryOn: "Network Retry:已开启",
180
+ retryOff: "Network Retry:已关闭",
181
+ retryHint: "请求异常时可自动重试并修复部分请求",
174
182
  syntheticInitiatorOn: "synthetic 消息按 agent 身份发送:已开启",
175
183
  syntheticInitiatorOff: "synthetic 消息按 agent 身份发送:已关闭",
176
184
  syntheticInitiatorHint: "会改变与 upstream 的默认行为;误用可能带来异常计费或 abuse 风险",
@@ -215,34 +223,30 @@ export function buildMenuItems(input) {
215
223
  capabilities.checkModels = false;
216
224
  capabilities.defaultAccountGroup = false;
217
225
  capabilities.assignModels = false;
218
- capabilities.loopSafety = false;
219
- capabilities.policyScope = false;
220
- capabilities.experimentalSlashCommands = false;
221
- capabilities.networkRetry = false;
222
226
  capabilities.syntheticAgentInitiator = false;
223
227
  }
224
228
  const quotaHint = input.lastQuotaRefresh ? `last ${formatRelativeTime(input.lastQuotaRefresh)}` : undefined;
225
229
  const loopSafetyProviderScope = input.loopSafetyProviderScope ?? "copilot-only";
226
230
  const experimentalSlashCommandsEnabled = input.experimentalSlashCommandsEnabled !== false;
227
- const actions = [
231
+ const providerActions = [
228
232
  { label: copy.actionsHeading, value: { type: "cancel" }, kind: "heading" },
229
233
  { label: copy.switchLanguageLabel, value: { type: "toggle-language" }, color: "cyan" },
230
234
  { label: copy.addAccount, value: { type: "add" }, color: "cyan", hint: copy.addAccountHint },
231
235
  ];
232
236
  if (capabilities.importAuth) {
233
- actions.push({ label: copy.importAuth, value: { type: "import" }, color: "cyan" });
237
+ providerActions.push({ label: copy.importAuth, value: { type: "import" }, color: "cyan" });
234
238
  }
235
239
  if (capabilities.quota) {
236
- actions.push({ label: copy.checkQuotas, value: { type: "quota" }, color: "cyan", hint: quotaHint });
240
+ providerActions.push({ label: copy.checkQuotas, value: { type: "quota" }, color: "cyan", hint: quotaHint });
237
241
  }
238
242
  if (capabilities.refreshIdentity) {
239
- actions.push({ label: copy.refreshIdentity, value: { type: "refresh-identity" }, color: "cyan" });
243
+ providerActions.push({ label: copy.refreshIdentity, value: { type: "refresh-identity" }, color: "cyan" });
240
244
  }
241
245
  if (capabilities.checkModels) {
242
- actions.push({ label: copy.checkModels, value: { type: "check-models" }, color: "cyan" });
246
+ providerActions.push({ label: copy.checkModels, value: { type: "check-models" }, color: "cyan" });
243
247
  }
244
248
  if (capabilities.defaultAccountGroup) {
245
- actions.push({
249
+ providerActions.push({
246
250
  label: copy.defaultAccountGroup,
247
251
  value: { type: "configure-default-group" },
248
252
  color: "cyan",
@@ -250,54 +254,56 @@ export function buildMenuItems(input) {
250
254
  });
251
255
  }
252
256
  if (capabilities.assignModels) {
253
- actions.push({
257
+ providerActions.push({
254
258
  label: copy.assignModels,
255
259
  value: { type: "assign-models" },
256
260
  color: "cyan",
257
261
  hint: input.modelAccountAssignmentCount ? `${input.modelAccountAssignmentCount} groups` : undefined,
258
262
  });
259
263
  }
260
- actions.push({
261
- label: input.refresh?.enabled ? copy.autoRefreshOn : copy.autoRefreshOff,
262
- value: { type: "toggle-refresh" },
263
- color: "cyan",
264
- hint: input.refresh ? `${input.refresh.minutes}m` : undefined,
265
- });
266
- actions.push({ label: copy.setRefresh, value: { type: "set-interval" }, color: "cyan" });
267
- if (capabilities.loopSafety) {
268
- actions.push({
264
+ const commonSettings = [
265
+ { label: copy.commonSettingsHeading, value: { type: "cancel" }, kind: "heading" },
266
+ {
269
267
  label: input.loopSafetyEnabled ? copy.loopSafetyOn : copy.loopSafetyOff,
270
268
  value: { type: "toggle-loop-safety" },
271
269
  color: "cyan",
272
270
  hint: copy.loopSafetyHint,
273
- });
274
- }
275
- if (capabilities.policyScope) {
276
- actions.push({
271
+ disabled: !capabilities.loopSafety,
272
+ },
273
+ {
277
274
  label: loopSafetyProviderScope === "all-models" ? copy.policyScopeAllModels : copy.policyScopeCopilotOnly,
278
275
  value: { type: "toggle-loop-safety-provider-scope" },
279
276
  color: "cyan",
280
277
  hint: copy.policyScopeHint,
281
- });
282
- }
283
- if (capabilities.experimentalSlashCommands) {
284
- actions.push({
278
+ disabled: !capabilities.policyScope,
279
+ },
280
+ {
285
281
  label: experimentalSlashCommandsEnabled ? copy.experimentalSlashCommandsOn : copy.experimentalSlashCommandsOff,
286
282
  value: { type: "toggle-experimental-slash-commands" },
287
283
  color: "cyan",
288
284
  hint: copy.experimentalSlashCommandsHint,
289
- });
290
- }
291
- if (capabilities.networkRetry) {
292
- actions.push({
285
+ disabled: !capabilities.experimentalSlashCommands,
286
+ },
287
+ {
293
288
  label: input.networkRetryEnabled ? copy.retryOn : copy.retryOff,
294
289
  value: { type: "toggle-network-retry" },
295
290
  color: "cyan",
296
291
  hint: copy.retryHint,
297
- });
298
- }
292
+ disabled: !capabilities.networkRetry,
293
+ },
294
+ ];
295
+ const providerSettings = [
296
+ { label: copy.providerSettingsHeading, value: { type: "cancel" }, kind: "heading" },
297
+ ];
298
+ providerSettings.push({
299
+ label: input.refresh?.enabled ? copy.autoRefreshOn : copy.autoRefreshOff,
300
+ value: { type: "toggle-refresh" },
301
+ color: "cyan",
302
+ hint: input.refresh ? `${input.refresh.minutes}m` : undefined,
303
+ });
304
+ providerSettings.push({ label: copy.setRefresh, value: { type: "set-interval" }, color: "cyan" });
299
305
  if (capabilities.syntheticAgentInitiator) {
300
- actions.push({
306
+ providerSettings.push({
301
307
  label: input.syntheticAgentInitiatorEnabled ? copy.syntheticInitiatorOn : copy.syntheticInitiatorOff,
302
308
  value: { type: "toggle-synthetic-agent-initiator" },
303
309
  color: "cyan",
@@ -305,7 +311,11 @@ export function buildMenuItems(input) {
305
311
  });
306
312
  }
307
313
  return [
308
- ...actions,
314
+ ...providerActions,
315
+ { label: "", value: { type: "cancel" }, separator: true },
316
+ ...commonSettings,
317
+ { label: "", value: { type: "cancel" }, separator: true },
318
+ ...providerSettings,
309
319
  { label: "", value: { type: "cancel" }, separator: true },
310
320
  { label: copy.accountsHeading, value: { type: "cancel" }, kind: "heading" },
311
321
  ...input.accounts.map((account) => {
@@ -318,6 +328,7 @@ export function buildMenuItems(input) {
318
328
  const numbered = `${account.index + 1}. ${account.name}`;
319
329
  const label = `${numbered}${currentBadge}${statusBadge ? " " + statusBadge : ""}${quotaBadge}`;
320
330
  const detail = [
331
+ account.workspaceName,
321
332
  account.lastUsed ? formatRelativeTime(account.lastUsed) : undefined,
322
333
  account.plan,
323
334
  account.models ? `${account.models.enabled}/${account.models.enabled + account.models.disabled} mods` : undefined,
@@ -0,0 +1,69 @@
1
+ export type CodexAuthState = {
2
+ type: string;
3
+ refresh?: string;
4
+ access?: string;
5
+ expires?: number;
6
+ accountId?: string;
7
+ };
8
+ export type CodexProviderModel = {
9
+ id?: string;
10
+ api?: {
11
+ id?: string;
12
+ url?: string;
13
+ npm?: string;
14
+ };
15
+ cost?: {
16
+ input: number;
17
+ output: number;
18
+ cache: {
19
+ read: number;
20
+ write: number;
21
+ };
22
+ };
23
+ };
24
+ export type CodexProviderConfig = {
25
+ models: Record<string, CodexProviderModel>;
26
+ };
27
+ export type OfficialCodexConfig = {
28
+ apiKey: string;
29
+ fetch: (request: Request | URL | string, init?: RequestInit) => Promise<Response>;
30
+ };
31
+ export type OfficialCodexChatHeadersHook = (input: {
32
+ sessionID: string;
33
+ model: {
34
+ providerID: string;
35
+ };
36
+ }, output: {
37
+ headers: Record<string, string>;
38
+ }) => Promise<void>;
39
+ export declare function loadOfficialCodexConfig(input: {
40
+ getAuth: () => Promise<CodexAuthState | undefined>;
41
+ provider?: CodexProviderConfig;
42
+ baseFetch?: typeof fetch;
43
+ version?: string;
44
+ client?: {
45
+ auth?: {
46
+ set?: (value: unknown) => Promise<unknown>;
47
+ };
48
+ };
49
+ }): Promise<OfficialCodexConfig | undefined>;
50
+ export declare function createOfficialCodexFetchAdapter(input: {
51
+ getAuth: () => Promise<CodexAuthState | undefined>;
52
+ provider?: CodexProviderConfig;
53
+ baseFetch?: typeof fetch;
54
+ version?: string;
55
+ client?: {
56
+ auth?: {
57
+ set?: (value: unknown) => Promise<unknown>;
58
+ };
59
+ };
60
+ }): (request: Request | URL | string, init?: RequestInit) => Promise<Response>;
61
+ export declare function loadOfficialCodexChatHeaders(input?: {
62
+ client?: {
63
+ auth?: {
64
+ set?: (value: unknown) => Promise<unknown>;
65
+ };
66
+ };
67
+ baseFetch?: typeof fetch;
68
+ version?: string;
69
+ }): Promise<OfficialCodexChatHeadersHook>;
@@ -0,0 +1,55 @@
1
+ import { CodexAuthPlugin, officialCodexExportBridge } from "./codex-plugin.snapshot.js";
2
+ function runWithOfficialBridge(input, fn) {
3
+ return officialCodexExportBridge.run({
4
+ fetchImpl: input.baseFetch ?? globalThis.fetch,
5
+ version: input.version,
6
+ }, fn);
7
+ }
8
+ async function loadOfficialHooks(input) {
9
+ return runWithOfficialBridge(input, async () => {
10
+ const hooks = await CodexAuthPlugin({
11
+ client: input.client,
12
+ });
13
+ return hooks;
14
+ });
15
+ }
16
+ export async function loadOfficialCodexConfig(input) {
17
+ const hooks = await loadOfficialHooks({
18
+ client: input.client,
19
+ baseFetch: input.baseFetch,
20
+ version: input.version,
21
+ });
22
+ const loader = hooks.auth?.loader;
23
+ if (typeof loader !== "function") {
24
+ return undefined;
25
+ }
26
+ const provider = input.provider ?? { models: {} };
27
+ const result = await runWithOfficialBridge(input, async () => loader(input.getAuth, provider));
28
+ if (!("fetch" in result) || typeof result.fetch !== "function") {
29
+ return undefined;
30
+ }
31
+ return {
32
+ apiKey: result.apiKey,
33
+ fetch(request, init) {
34
+ return runWithOfficialBridge(input, async () => result.fetch(request, init));
35
+ },
36
+ };
37
+ }
38
+ export function createOfficialCodexFetchAdapter(input) {
39
+ return async function fetchWithOfficialHeaders(request, init) {
40
+ const config = await loadOfficialCodexConfig(input);
41
+ const fallback = input.baseFetch ?? fetch;
42
+ if (!config) {
43
+ return fallback(request, init);
44
+ }
45
+ return config.fetch(request, init);
46
+ };
47
+ }
48
+ export async function loadOfficialCodexChatHeaders(input = {}) {
49
+ const hooks = await loadOfficialHooks(input);
50
+ const chatHeaders = hooks["chat.headers"];
51
+ if (typeof chatHeaders !== "function") {
52
+ throw new Error("Official Codex plugin is missing chat.headers hook");
53
+ }
54
+ return async (hookInput, output) => runWithOfficialBridge(input, async () => chatHeaders(hookInput, output));
55
+ }
@@ -0,0 +1,13 @@
1
+ type RequestInfo = Request | URL | string;
2
+ type Hooks = any;
3
+ type PluginInput = any;
4
+ declare const officialCodexExportBridge: {
5
+ version: string;
6
+ fetchImpl(request: RequestInfo | URL, init?: RequestInit): Promise<Response>;
7
+ run(options: {
8
+ fetchImpl?: typeof globalThis.fetch;
9
+ version?: string;
10
+ } | undefined, fn: () => Promise<any>): Promise<any>;
11
+ };
12
+ export declare function CodexAuthPlugin(input: PluginInput): Promise<Hooks>;
13
+ export { officialCodexExportBridge };
@@ -0,0 +1,98 @@
1
+ // @ts-nocheck
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import os from "node:os";
4
+ const officialCodexExportBridgeStorage = new AsyncLocalStorage();
5
+ const officialCodexExportBridge = {
6
+ version: "snapshot",
7
+ fetchImpl(request, init) {
8
+ return globalThis.fetch(request, init);
9
+ },
10
+ async run(options = {}, fn) {
11
+ return officialCodexExportBridgeStorage.run({
12
+ fetchImpl: options.fetchImpl ?? this.fetchImpl,
13
+ version: options.version ?? this.version,
14
+ }, fn);
15
+ },
16
+ };
17
+ const Installation = {
18
+ get VERSION() {
19
+ return officialCodexExportBridgeStorage.getStore()?.version ?? officialCodexExportBridge.version;
20
+ },
21
+ set VERSION(value) {
22
+ officialCodexExportBridge.version = value;
23
+ },
24
+ };
25
+ const OAUTH_DUMMY_KEY = "official-codex-oauth";
26
+ function fetch(request, init) {
27
+ return (officialCodexExportBridgeStorage.getStore()?.fetchImpl ?? officialCodexExportBridge.fetchImpl)(request, init);
28
+ }
29
+ /* LOCAL_SHIMS_END */
30
+ const CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
31
+ export async function CodexAuthPlugin(input) {
32
+ return {
33
+ auth: {
34
+ provider: "openai",
35
+ async loader(getAuth, provider) {
36
+ const auth = await getAuth();
37
+ if (auth.type !== "oauth")
38
+ return {};
39
+ if (provider?.models) {
40
+ for (const model of Object.values(provider.models)) {
41
+ model.cost = {
42
+ input: 0,
43
+ output: 0,
44
+ cache: { read: 0, write: 0 },
45
+ };
46
+ }
47
+ }
48
+ return {
49
+ apiKey: OAUTH_DUMMY_KEY,
50
+ async fetch(requestInput, init) {
51
+ const currentAuth = await getAuth();
52
+ if (currentAuth.type !== "oauth")
53
+ return fetch(requestInput, init);
54
+ const headers = new Headers(init?.headers);
55
+ headers.delete("authorization");
56
+ headers.delete("Authorization");
57
+ headers.set("authorization", `Bearer ${currentAuth.access}`);
58
+ if (currentAuth.accountId) {
59
+ headers.set("ChatGPT-Account-Id", currentAuth.accountId);
60
+ }
61
+ const parsed = requestInput instanceof URL
62
+ ? requestInput
63
+ : new URL(typeof requestInput === "string" ? requestInput : requestInput.url);
64
+ const url = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions")
65
+ ? new URL(CODEX_API_ENDPOINT)
66
+ : parsed;
67
+ return fetch(url, {
68
+ ...init,
69
+ headers,
70
+ });
71
+ },
72
+ };
73
+ },
74
+ methods: [
75
+ {
76
+ label: "ChatGPT Pro/Plus (browser)",
77
+ type: "oauth",
78
+ authorize: async () => ({
79
+ url: "",
80
+ instructions: "",
81
+ method: "auto",
82
+ callback: async () => ({ type: "failed" }),
83
+ }),
84
+ },
85
+ ],
86
+ },
87
+ "chat.headers": async (input, output) => {
88
+ if (input.model.providerID !== "openai")
89
+ return;
90
+ output.headers.originator = "opencode";
91
+ output.headers["User-Agent"] = `opencode/${Installation.VERSION} (${os.platform()} ${os.release()}; ${os.arch()})`;
92
+ output.headers.session_id = input.sessionID;
93
+ },
94
+ };
95
+ }
96
+ /* GENERATED_EXPORT_BRIDGE_START */
97
+ export { officialCodexExportBridge };
98
+ /* GENERATED_EXPORT_BRIDGE_END */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-copilot-account-switcher",
3
- "version": "0.13.3",
3
+ "version": "0.13.5",
4
4
  "description": "GitHub Copilot account switcher plugin for OpenCode",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -44,6 +44,8 @@
44
44
  "build": "tsc -p tsconfig.build.json",
45
45
  "sync:copilot-snapshot": "node scripts/sync-copilot-upstream.mjs --output src/upstream/copilot-plugin.snapshot.ts",
46
46
  "check:copilot-sync": "node scripts/sync-copilot-upstream.mjs --output src/upstream/copilot-plugin.snapshot.ts --check",
47
+ "sync:codex-snapshot": "node scripts/sync-codex-upstream.mjs --output src/upstream/codex-plugin.snapshot.ts",
48
+ "check:codex-sync": "node scripts/sync-codex-upstream.mjs --output src/upstream/codex-plugin.snapshot.ts --check",
47
49
  "test": "npm run build && node --test",
48
50
  "typecheck": "tsc --noEmit",
49
51
  "prepublishOnly": "npm run build"