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
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { CODEX_PROVIDER_DESCRIPTOR, COPILOT_PROVIDER_DESCRIPTOR } from "./descriptor.js";
|
|
2
2
|
import { createCodexProviderDescriptor, createCopilotProviderDescriptor } from "./descriptor.js";
|
|
3
|
+
import { createCopilotRetryingFetch } from "../copilot-network-retry.js";
|
|
4
|
+
import { createCodexRetryingFetch } from "../codex-network-retry.js";
|
|
5
|
+
import { loadOfficialCopilotChatHeaders, loadOfficialCopilotConfig } from "../upstream/copilot-loader-adapter.js";
|
|
6
|
+
import { loadOfficialCodexChatHeaders, loadOfficialCodexConfig } from "../upstream/codex-loader-adapter.js";
|
|
3
7
|
const PROVIDER_DESCRIPTORS = [
|
|
4
8
|
COPILOT_PROVIDER_DESCRIPTOR,
|
|
5
9
|
CODEX_PROVIDER_DESCRIPTOR,
|
|
@@ -16,13 +20,45 @@ export function getProviderDescriptorByProviderID(providerID) {
|
|
|
16
20
|
export function isProviderIDSupportedByAnyDescriptor(providerID) {
|
|
17
21
|
return getProviderDescriptorByProviderID(providerID) !== undefined;
|
|
18
22
|
}
|
|
23
|
+
function hasCapability(descriptor, capability) {
|
|
24
|
+
return descriptor.capabilities.includes(capability);
|
|
25
|
+
}
|
|
19
26
|
export function createProviderRegistry(input) {
|
|
27
|
+
const copilotCapabilities = COPILOT_PROVIDER_DESCRIPTOR;
|
|
28
|
+
const codexCapabilities = CODEX_PROVIDER_DESCRIPTOR;
|
|
29
|
+
const buildCopilotPluginHooks = (hookInput) => input.buildPluginHooks({
|
|
30
|
+
...hookInput,
|
|
31
|
+
authLoaderMode: hasCapability(copilotCapabilities, "auth") ? "copilot" : "none",
|
|
32
|
+
enableModelRouting: hasCapability(copilotCapabilities, "model-routing"),
|
|
33
|
+
loadOfficialConfig: hasCapability(copilotCapabilities, "auth") ? loadOfficialCopilotConfig : undefined,
|
|
34
|
+
loadOfficialChatHeaders: hasCapability(copilotCapabilities, "chat-headers") ? loadOfficialCopilotChatHeaders : undefined,
|
|
35
|
+
createRetryFetch: hasCapability(copilotCapabilities, "network-retry") ? createCopilotRetryingFetch : undefined,
|
|
36
|
+
});
|
|
37
|
+
const buildCodexPluginHooks = (hookInput) => input.buildPluginHooks({
|
|
38
|
+
...hookInput,
|
|
39
|
+
authLoaderMode: hasCapability(codexCapabilities, "auth") ? "codex" : "none",
|
|
40
|
+
enableModelRouting: hasCapability(codexCapabilities, "model-routing"),
|
|
41
|
+
loadOfficialConfig: hasCapability(codexCapabilities, "auth")
|
|
42
|
+
? ({ getAuth, baseFetch, version }) => loadOfficialCodexConfig({
|
|
43
|
+
getAuth: getAuth,
|
|
44
|
+
baseFetch,
|
|
45
|
+
version,
|
|
46
|
+
client: hookInput.client,
|
|
47
|
+
})
|
|
48
|
+
: undefined,
|
|
49
|
+
loadOfficialChatHeaders: hasCapability(codexCapabilities, "chat-headers")
|
|
50
|
+
? loadOfficialCodexChatHeaders
|
|
51
|
+
: undefined,
|
|
52
|
+
createRetryFetch: hasCapability(codexCapabilities, "network-retry")
|
|
53
|
+
? createCodexRetryingFetch
|
|
54
|
+
: undefined,
|
|
55
|
+
});
|
|
20
56
|
return {
|
|
21
57
|
copilot: {
|
|
22
|
-
descriptor: createCopilotProviderDescriptor({ buildPluginHooks:
|
|
58
|
+
descriptor: createCopilotProviderDescriptor({ buildPluginHooks: buildCopilotPluginHooks }),
|
|
23
59
|
},
|
|
24
60
|
codex: {
|
|
25
|
-
descriptor: createCodexProviderDescriptor({ enabled: true }),
|
|
61
|
+
descriptor: createCodexProviderDescriptor({ buildPluginHooks: buildCodexPluginHooks, enabled: true }),
|
|
26
62
|
},
|
|
27
63
|
};
|
|
28
64
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { NetworkRetryPolicy } from "../network-retry-engine.js";
|
|
2
|
+
export declare const CODEX_RETRYABLE_MESSAGES: string[];
|
|
3
|
+
export type CodexRetryableErrorGroup = "transport" | "status";
|
|
4
|
+
export declare function isRetryableCodexTransportError(error: unknown): boolean;
|
|
5
|
+
export declare function createCodexRetryPolicy(): NetworkRetryPolicy;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createMessageRetryClassifier, isAbortError, isRetryableApiCallError, normalizeRetryableStatusResponse, toRequestUrl, toRetryableApiCallError, } from "./common-policy.js";
|
|
2
|
+
export const CODEX_RETRYABLE_MESSAGES = [
|
|
3
|
+
"load failed",
|
|
4
|
+
"failed to fetch",
|
|
5
|
+
"network request failed",
|
|
6
|
+
"unable to connect",
|
|
7
|
+
"connection reset",
|
|
8
|
+
"connection aborted",
|
|
9
|
+
"econnreset",
|
|
10
|
+
"etimedout",
|
|
11
|
+
"timeout",
|
|
12
|
+
"socket hang up",
|
|
13
|
+
];
|
|
14
|
+
function isCodexUrl(request) {
|
|
15
|
+
const raw = toRequestUrl(request);
|
|
16
|
+
try {
|
|
17
|
+
const url = new URL(raw);
|
|
18
|
+
return url.hostname === "chatgpt.com" && url.pathname.startsWith("/backend-api/codex/");
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const isRetryableCodexTransportErrorByMessage = createMessageRetryClassifier({
|
|
25
|
+
retryableMessages: CODEX_RETRYABLE_MESSAGES,
|
|
26
|
+
isAbortError,
|
|
27
|
+
});
|
|
28
|
+
export function isRetryableCodexTransportError(error) {
|
|
29
|
+
return isRetryableCodexTransportErrorByMessage(error);
|
|
30
|
+
}
|
|
31
|
+
function isRetryableCodexStatus(status) {
|
|
32
|
+
if (status === 429)
|
|
33
|
+
return true;
|
|
34
|
+
return status >= 500 && status <= 599;
|
|
35
|
+
}
|
|
36
|
+
function normalizeRetryableStatusResponseForCodex(response, request) {
|
|
37
|
+
return normalizeRetryableStatusResponse({
|
|
38
|
+
response,
|
|
39
|
+
request,
|
|
40
|
+
providerLabel: "Codex",
|
|
41
|
+
group: "status",
|
|
42
|
+
isRetryableStatus: isRetryableCodexStatus,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function toCodexRetryableApiCallError(error, request, options) {
|
|
46
|
+
return toRetryableApiCallError(error, request, {
|
|
47
|
+
providerLabel: "Codex",
|
|
48
|
+
group: options?.group ?? "transport",
|
|
49
|
+
statusCode: options?.statusCode,
|
|
50
|
+
responseHeaders: options?.responseHeaders,
|
|
51
|
+
responseBody: options?.responseBody,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export function createCodexRetryPolicy() {
|
|
55
|
+
return {
|
|
56
|
+
matchesRequest: (request) => isCodexUrl(request),
|
|
57
|
+
classifyFailure: async ({ error }) => {
|
|
58
|
+
if (isRetryableApiCallError(error)) {
|
|
59
|
+
return { retryable: false, category: "already-normalized" };
|
|
60
|
+
}
|
|
61
|
+
if (isRetryableCodexTransportError(error)) {
|
|
62
|
+
return { retryable: true, category: "transport" };
|
|
63
|
+
}
|
|
64
|
+
return { retryable: false, category: "none" };
|
|
65
|
+
},
|
|
66
|
+
handleResponse: async ({ response, request }) => normalizeRetryableStatusResponseForCodex(response, request),
|
|
67
|
+
normalizeFailure: ({ error, classification, request }) => {
|
|
68
|
+
if (classification.retryable && classification.category === "transport") {
|
|
69
|
+
return toCodexRetryableApiCallError(error, request);
|
|
70
|
+
}
|
|
71
|
+
return error;
|
|
72
|
+
},
|
|
73
|
+
buildRepairPlan: async () => undefined,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { NetworkRetryRequest } from "../network-retry-engine.js";
|
|
2
|
+
export type RetryableApiCallError = Error & {
|
|
3
|
+
url: string;
|
|
4
|
+
requestBodyValues: unknown;
|
|
5
|
+
statusCode?: number;
|
|
6
|
+
responseHeaders?: Record<string, string>;
|
|
7
|
+
responseBody?: string;
|
|
8
|
+
isRetryable: boolean;
|
|
9
|
+
cause: unknown;
|
|
10
|
+
[key: symbol]: unknown;
|
|
11
|
+
};
|
|
12
|
+
export declare function toRequestUrl(request: Request | URL | string): string;
|
|
13
|
+
export declare function getErrorMessage(error: unknown): string;
|
|
14
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
15
|
+
export declare function createMessageRetryClassifier(options: {
|
|
16
|
+
retryableMessages: string[];
|
|
17
|
+
isAbortError?: (error: unknown) => boolean;
|
|
18
|
+
}): (error: unknown) => boolean;
|
|
19
|
+
export declare function toRetryableApiCallError(error: unknown, request: {
|
|
20
|
+
url: string;
|
|
21
|
+
body?: string;
|
|
22
|
+
}, options: {
|
|
23
|
+
providerLabel: string;
|
|
24
|
+
group: string;
|
|
25
|
+
requestBodyValues?: unknown;
|
|
26
|
+
statusCode?: number;
|
|
27
|
+
responseHeaders?: Headers | Record<string, string>;
|
|
28
|
+
responseBody?: string;
|
|
29
|
+
}): RetryableApiCallError;
|
|
30
|
+
export declare function isRetryableApiCallError(error: unknown): error is RetryableApiCallError;
|
|
31
|
+
export declare function normalizeRetryableStatusResponse(input: {
|
|
32
|
+
response: Response;
|
|
33
|
+
request: NetworkRetryRequest;
|
|
34
|
+
providerLabel: string;
|
|
35
|
+
group: string;
|
|
36
|
+
isRetryableStatus: (status: number) => boolean;
|
|
37
|
+
}): Promise<Response>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const AI_ERROR_MARKER = Symbol.for("vercel.ai.error");
|
|
2
|
+
const API_CALL_ERROR_MARKER = Symbol.for("vercel.ai.error.AI_APICallError");
|
|
3
|
+
export function toRequestUrl(request) {
|
|
4
|
+
return request instanceof Request ? request.url : request instanceof URL ? request.href : String(request);
|
|
5
|
+
}
|
|
6
|
+
export function getErrorMessage(error) {
|
|
7
|
+
return String(error instanceof Error ? error.message : error).toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
export function isAbortError(error) {
|
|
10
|
+
return error instanceof Error && error.name === "AbortError";
|
|
11
|
+
}
|
|
12
|
+
export function createMessageRetryClassifier(options) {
|
|
13
|
+
return function isRetryableByMessage(error) {
|
|
14
|
+
if (!error)
|
|
15
|
+
return false;
|
|
16
|
+
if (options.isAbortError?.(error) ?? isAbortError(error))
|
|
17
|
+
return false;
|
|
18
|
+
const message = getErrorMessage(error);
|
|
19
|
+
return options.retryableMessages.some((part) => message.includes(part));
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function buildRetryableApiCallMessage(providerLabel, group, detail) {
|
|
23
|
+
return `${providerLabel} retryable error [${group}]: ${detail}`;
|
|
24
|
+
}
|
|
25
|
+
export function toRetryableApiCallError(error, request, options) {
|
|
26
|
+
const base = error instanceof Error ? error : new Error(String(error));
|
|
27
|
+
const wrapped = new Error(buildRetryableApiCallMessage(options.providerLabel, options.group, base.message));
|
|
28
|
+
wrapped.name = "AI_APICallError";
|
|
29
|
+
wrapped.url = request.url;
|
|
30
|
+
wrapped.requestBodyValues = options.requestBodyValues ?? (() => {
|
|
31
|
+
if (!request.body)
|
|
32
|
+
return {};
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(request.body);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
wrapped.statusCode = options.statusCode;
|
|
41
|
+
wrapped.responseHeaders = options.responseHeaders instanceof Headers
|
|
42
|
+
? Object.fromEntries(options.responseHeaders.entries())
|
|
43
|
+
: options.responseHeaders;
|
|
44
|
+
wrapped.responseBody = options.responseBody;
|
|
45
|
+
wrapped.isRetryable = true;
|
|
46
|
+
wrapped.cause = error;
|
|
47
|
+
wrapped[AI_ERROR_MARKER] = true;
|
|
48
|
+
wrapped[API_CALL_ERROR_MARKER] = true;
|
|
49
|
+
return wrapped;
|
|
50
|
+
}
|
|
51
|
+
export function isRetryableApiCallError(error) {
|
|
52
|
+
return Boolean(error
|
|
53
|
+
&& typeof error === "object"
|
|
54
|
+
&& error[AI_ERROR_MARKER] === true
|
|
55
|
+
&& error[API_CALL_ERROR_MARKER] === true);
|
|
56
|
+
}
|
|
57
|
+
export async function normalizeRetryableStatusResponse(input) {
|
|
58
|
+
if (!input.isRetryableStatus(input.response.status))
|
|
59
|
+
return input.response;
|
|
60
|
+
const responseBody = await input.response.clone().text().catch(() => "");
|
|
61
|
+
throw toRetryableApiCallError(new Error(responseBody || `status code ${input.response.status}`), input.request, {
|
|
62
|
+
providerLabel: input.providerLabel,
|
|
63
|
+
group: input.group,
|
|
64
|
+
statusCode: input.response.status,
|
|
65
|
+
responseHeaders: input.response.headers,
|
|
66
|
+
responseBody: responseBody || undefined,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import type { NetworkRetryPolicy } from "../network-retry-engine.js";
|
|
2
|
+
import { type RetryableApiCallError } from "./common-policy.js";
|
|
2
3
|
export declare const COPILOT_RETRYABLE_MESSAGES: string[];
|
|
3
4
|
export type RetryableErrorGroup = "transport" | "status" | "stream";
|
|
4
|
-
type RetryableApiCallError = Error & {
|
|
5
|
-
url: string;
|
|
6
|
-
requestBodyValues: unknown;
|
|
7
|
-
statusCode?: number;
|
|
8
|
-
responseHeaders?: Record<string, string>;
|
|
9
|
-
responseBody?: string;
|
|
10
|
-
isRetryable: boolean;
|
|
11
|
-
cause: unknown;
|
|
12
|
-
[key: symbol]: unknown;
|
|
13
|
-
};
|
|
14
5
|
type CopilotStreamErrorInput = {
|
|
15
6
|
error: unknown;
|
|
16
7
|
request: Request | URL | string;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const API_CALL_ERROR_MARKER = Symbol.for("vercel.ai.error.AI_APICallError");
|
|
1
|
+
import { createMessageRetryClassifier, getErrorMessage, isAbortError, isRetryableApiCallError as isRetryableApiCallErrorByMarker, normalizeRetryableStatusResponse, toRequestUrl, toRetryableApiCallError as toRetryableApiCallErrorByCommon, } from "./common-policy.js";
|
|
3
2
|
export const COPILOT_RETRYABLE_MESSAGES = [
|
|
4
3
|
"load failed",
|
|
5
4
|
"failed to fetch",
|
|
@@ -13,15 +12,6 @@ export const COPILOT_RETRYABLE_MESSAGES = [
|
|
|
13
12
|
"unable to verify the first certificate",
|
|
14
13
|
"self-signed certificate in certificate chain",
|
|
15
14
|
];
|
|
16
|
-
function toRequestUrl(request) {
|
|
17
|
-
return request instanceof Request ? request.url : request instanceof URL ? request.href : String(request);
|
|
18
|
-
}
|
|
19
|
-
function getErrorMessage(error) {
|
|
20
|
-
return String(error instanceof Error ? error.message : error).toLowerCase();
|
|
21
|
-
}
|
|
22
|
-
function isAbortError(error) {
|
|
23
|
-
return error instanceof Error && error.name === "AbortError";
|
|
24
|
-
}
|
|
25
15
|
function isSseReadTimeoutError(error) {
|
|
26
16
|
return getErrorMessage(error).includes("sse read timed out");
|
|
27
17
|
}
|
|
@@ -96,57 +86,33 @@ export function isCopilotUrl(request) {
|
|
|
96
86
|
return false;
|
|
97
87
|
}
|
|
98
88
|
}
|
|
89
|
+
const isRetryableCopilotTransportErrorByMessage = createMessageRetryClassifier({
|
|
90
|
+
retryableMessages: COPILOT_RETRYABLE_MESSAGES,
|
|
91
|
+
isAbortError,
|
|
92
|
+
});
|
|
99
93
|
export function isRetryableCopilotTransportError(error) {
|
|
100
|
-
|
|
101
|
-
return false;
|
|
102
|
-
const message = getErrorMessage(error);
|
|
103
|
-
return COPILOT_RETRYABLE_MESSAGES.some((part) => message.includes(part));
|
|
104
|
-
}
|
|
105
|
-
function buildRetryableApiCallMessage(group, detail) {
|
|
106
|
-
return `Copilot retryable error [${group}]: ${detail}`;
|
|
94
|
+
return isRetryableCopilotTransportErrorByMessage(error);
|
|
107
95
|
}
|
|
108
96
|
export function toRetryableApiCallError(error, request, options) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return JSON.parse(request.body);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return {};
|
|
121
|
-
}
|
|
122
|
-
})();
|
|
123
|
-
wrapped.statusCode = options?.statusCode;
|
|
124
|
-
wrapped.responseHeaders = options?.responseHeaders instanceof Headers
|
|
125
|
-
? Object.fromEntries(options.responseHeaders.entries())
|
|
126
|
-
: options?.responseHeaders;
|
|
127
|
-
wrapped.responseBody = options?.responseBody;
|
|
128
|
-
wrapped.isRetryable = true;
|
|
129
|
-
wrapped.cause = error;
|
|
130
|
-
wrapped[AI_ERROR_MARKER] = true;
|
|
131
|
-
wrapped[API_CALL_ERROR_MARKER] = true;
|
|
132
|
-
return wrapped;
|
|
97
|
+
return toRetryableApiCallErrorByCommon(error, request, {
|
|
98
|
+
providerLabel: "Copilot",
|
|
99
|
+
group: options?.group ?? "transport",
|
|
100
|
+
requestBodyValues: options?.requestBodyValues,
|
|
101
|
+
statusCode: options?.statusCode,
|
|
102
|
+
responseHeaders: options?.responseHeaders,
|
|
103
|
+
responseBody: options?.responseBody,
|
|
104
|
+
});
|
|
133
105
|
}
|
|
134
106
|
export function isRetryableApiCallError(error) {
|
|
135
|
-
return
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
throw toRetryableApiCallError(new Error(responseBody || `status code ${response.status}`), request, {
|
|
145
|
-
group: "status",
|
|
146
|
-
statusCode: response.status,
|
|
147
|
-
responseHeaders: response.headers,
|
|
148
|
-
responseBody: responseBody || undefined,
|
|
149
|
-
});
|
|
107
|
+
return isRetryableApiCallErrorByMarker(error);
|
|
108
|
+
}
|
|
109
|
+
function normalizeRetryableStatusResponseForCopilot(response, request) {
|
|
110
|
+
return normalizeRetryableStatusResponse({
|
|
111
|
+
response,
|
|
112
|
+
request,
|
|
113
|
+
providerLabel: "Copilot",
|
|
114
|
+
group: "status",
|
|
115
|
+
isRetryableStatus: (status) => status === 499,
|
|
150
116
|
});
|
|
151
117
|
}
|
|
152
118
|
export function createCopilotRetryPolicy(options) {
|
|
@@ -161,7 +127,7 @@ export function createCopilotRetryPolicy(options) {
|
|
|
161
127
|
}
|
|
162
128
|
return { retryable: false, category: "none" };
|
|
163
129
|
},
|
|
164
|
-
handleResponse: async ({ response, request }) =>
|
|
130
|
+
handleResponse: async ({ response, request }) => normalizeRetryableStatusResponseForCopilot(response, request),
|
|
165
131
|
normalizeFailure: ({ error, classification, request }) => {
|
|
166
132
|
if (classification.retryable && classification.category === "transport") {
|
|
167
133
|
return toRetryableApiCallError(error, request);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function accountSwitcherConfigDir(): string;
|
|
2
|
+
export declare function commonSettingsPath(): string;
|
|
3
|
+
export declare function copilotAccountsPath(): string;
|
|
4
|
+
export declare function codexAccountsPath(): string;
|
|
5
|
+
export declare function legacyCopilotStorePath(): string;
|
|
6
|
+
export declare function legacyCodexStorePath(): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { xdgConfig } from "xdg-basedir";
|
|
4
|
+
function configBaseDir() {
|
|
5
|
+
return xdgConfig ?? path.join(os.homedir(), ".config");
|
|
6
|
+
}
|
|
7
|
+
export function accountSwitcherConfigDir() {
|
|
8
|
+
return path.join(configBaseDir(), "opencode", "account-switcher");
|
|
9
|
+
}
|
|
10
|
+
export function commonSettingsPath() {
|
|
11
|
+
return path.join(accountSwitcherConfigDir(), "settings.json");
|
|
12
|
+
}
|
|
13
|
+
export function copilotAccountsPath() {
|
|
14
|
+
return path.join(accountSwitcherConfigDir(), "copilot-accounts.json");
|
|
15
|
+
}
|
|
16
|
+
export function codexAccountsPath() {
|
|
17
|
+
return path.join(accountSwitcherConfigDir(), "codex-accounts.json");
|
|
18
|
+
}
|
|
19
|
+
export function legacyCopilotStorePath() {
|
|
20
|
+
return path.join(configBaseDir(), "opencode", "copilot-accounts.json");
|
|
21
|
+
}
|
|
22
|
+
export function legacyCodexStorePath() {
|
|
23
|
+
return path.join(configBaseDir(), "opencode", "codex-store.json");
|
|
24
|
+
}
|
package/dist/store.js
CHANGED
|
@@ -3,7 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import { promises as fs } from "node:fs";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
5
|
import { xdgConfig, xdgData } from "xdg-basedir";
|
|
6
|
-
|
|
6
|
+
import { copilotAccountsPath, legacyCopilotStorePath } from "./store-paths.js";
|
|
7
7
|
const authFile = "auth.json";
|
|
8
8
|
const defaultStoreDebugLogFile = (() => {
|
|
9
9
|
const tmp = process.env.TEMP || process.env.TMP || "/tmp";
|
|
@@ -57,8 +57,7 @@ async function logStoreWrite(input) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
export function storePath() {
|
|
60
|
-
|
|
61
|
-
return path.join(base, "opencode", filename);
|
|
60
|
+
return copilotAccountsPath();
|
|
62
61
|
}
|
|
63
62
|
export function authPath() {
|
|
64
63
|
const dataDir = xdgData ?? path.join(os.homedir(), ".local", "share");
|
|
@@ -134,16 +133,32 @@ export function parseStore(raw) {
|
|
|
134
133
|
return store;
|
|
135
134
|
}
|
|
136
135
|
export async function readStore(filePath = storePath()) {
|
|
137
|
-
const raw = await fs.readFile(filePath, "utf-8").catch((error) => {
|
|
138
|
-
if (error.code
|
|
136
|
+
const raw = await fs.readFile(filePath, "utf-8").catch(async (error) => {
|
|
137
|
+
if (error.code !== "ENOENT")
|
|
138
|
+
throw error;
|
|
139
|
+
if (filePath !== storePath())
|
|
139
140
|
return "";
|
|
140
|
-
|
|
141
|
+
return fs.readFile(legacyCopilotStorePath(), "utf-8").catch((legacyError) => {
|
|
142
|
+
if (legacyError.code === "ENOENT")
|
|
143
|
+
return "";
|
|
144
|
+
throw legacyError;
|
|
145
|
+
});
|
|
141
146
|
});
|
|
142
147
|
return parseStore(raw);
|
|
143
148
|
}
|
|
144
149
|
export async function readStoreSafe(filePath = storePath()) {
|
|
145
150
|
try {
|
|
146
|
-
const raw = await fs.readFile(filePath, "utf-8")
|
|
151
|
+
const raw = await fs.readFile(filePath, "utf-8").catch(async (error) => {
|
|
152
|
+
if (error.code !== "ENOENT")
|
|
153
|
+
throw error;
|
|
154
|
+
if (filePath !== storePath())
|
|
155
|
+
return "";
|
|
156
|
+
return fs.readFile(legacyCopilotStorePath(), "utf-8").catch((legacyError) => {
|
|
157
|
+
if (legacyError.code === "ENOENT")
|
|
158
|
+
return "";
|
|
159
|
+
throw legacyError;
|
|
160
|
+
});
|
|
161
|
+
});
|
|
147
162
|
return parseStore(raw);
|
|
148
163
|
}
|
|
149
164
|
catch (error) {
|
|
@@ -159,8 +174,19 @@ export function readStoreSafeSync(filePath = storePath()) {
|
|
|
159
174
|
}
|
|
160
175
|
catch (error) {
|
|
161
176
|
const issue = error;
|
|
162
|
-
if (issue.code === "ENOENT")
|
|
177
|
+
if (issue.code === "ENOENT") {
|
|
178
|
+
try {
|
|
179
|
+
if (filePath === storePath()) {
|
|
180
|
+
return parseStore(readFileSync(legacyCopilotStorePath(), "utf-8"));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (legacyError) {
|
|
184
|
+
const legacyIssue = legacyError;
|
|
185
|
+
if (legacyIssue.code !== "ENOENT")
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
163
188
|
return parseStore("");
|
|
189
|
+
}
|
|
164
190
|
return undefined;
|
|
165
191
|
}
|
|
166
192
|
}
|
package/dist/ui/menu.d.ts
CHANGED
|
@@ -102,6 +102,8 @@ export declare function getMenuCopy(language?: MenuLanguage, provider?: MenuProv
|
|
|
102
102
|
menuSubtitle: string;
|
|
103
103
|
switchLanguageLabel: string;
|
|
104
104
|
actionsHeading: string;
|
|
105
|
+
commonSettingsHeading: string;
|
|
106
|
+
providerSettingsHeading: string;
|
|
105
107
|
addAccount: string;
|
|
106
108
|
addAccountHint: string;
|
|
107
109
|
importAuth: string;
|