oc-codex-multi-auth 6.1.2 → 6.1.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/README.md +27 -23
- package/config/README.md +35 -35
- package/config/opencode-legacy.json +80 -30
- package/config/opencode-modern.json +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +157 -11
- package/dist/index.js.map +1 -1
- package/dist/lib/accounts/persistence.d.ts.map +1 -1
- package/dist/lib/accounts/persistence.js +1 -0
- package/dist/lib/accounts/persistence.js.map +1 -1
- package/dist/lib/accounts/state.d.ts +2 -2
- package/dist/lib/accounts/state.d.ts.map +1 -1
- package/dist/lib/accounts/state.js +87 -19
- package/dist/lib/accounts/state.js.map +1 -1
- package/dist/lib/auth/auth.d.ts +2 -2
- package/dist/lib/auth/auth.d.ts.map +1 -1
- package/dist/lib/auth/auth.js +9 -6
- package/dist/lib/auth/auth.js.map +1 -1
- package/dist/lib/auth/login-runner.d.ts +1 -0
- package/dist/lib/auth/login-runner.d.ts.map +1 -1
- package/dist/lib/auth/login-runner.js +3 -0
- package/dist/lib/auth/login-runner.js.map +1 -1
- package/dist/lib/auth/scopes.d.ts +5 -0
- package/dist/lib/auth/scopes.d.ts.map +1 -0
- package/dist/lib/auth/scopes.js +23 -0
- package/dist/lib/auth/scopes.js.map +1 -0
- package/dist/lib/auth/server.d.ts +6 -1
- package/dist/lib/auth/server.d.ts.map +1 -1
- package/dist/lib/auth/server.js +95 -51
- package/dist/lib/auth/server.js.map +1 -1
- package/dist/lib/codex-usage.d.ts +91 -0
- package/dist/lib/codex-usage.d.ts.map +1 -0
- package/dist/lib/codex-usage.js +368 -0
- package/dist/lib/codex-usage.js.map +1 -0
- package/dist/lib/oauth-constants.d.ts +5 -5
- package/dist/lib/oauth-constants.d.ts.map +1 -1
- package/dist/lib/oauth-constants.js +5 -5
- package/dist/lib/oauth-constants.js.map +1 -1
- package/dist/lib/prompts/codex.d.ts +2 -0
- package/dist/lib/prompts/codex.d.ts.map +1 -1
- package/dist/lib/prompts/codex.js +43 -11
- package/dist/lib/prompts/codex.js.map +1 -1
- package/dist/lib/request/fetch-helpers.d.ts +2 -2
- package/dist/lib/request/fetch-helpers.d.ts.map +1 -1
- package/dist/lib/request/fetch-helpers.js +37 -25
- package/dist/lib/request/fetch-helpers.js.map +1 -1
- package/dist/lib/request/helpers/model-map.d.ts +1 -0
- package/dist/lib/request/helpers/model-map.d.ts.map +1 -1
- package/dist/lib/request/helpers/model-map.js +28 -25
- package/dist/lib/request/helpers/model-map.js.map +1 -1
- package/dist/lib/request/request-transformer.d.ts +1 -0
- package/dist/lib/request/request-transformer.d.ts.map +1 -1
- package/dist/lib/request/request-transformer.js +46 -22
- package/dist/lib/request/request-transformer.js.map +1 -1
- package/dist/lib/schemas.d.ts +8 -0
- package/dist/lib/schemas.d.ts.map +1 -1
- package/dist/lib/schemas.js +3 -0
- package/dist/lib/schemas.js.map +1 -1
- package/dist/lib/storage/migrations.d.ts +4 -0
- package/dist/lib/storage/migrations.d.ts.map +1 -1
- package/dist/lib/storage/migrations.js +1 -0
- package/dist/lib/storage/migrations.js.map +1 -1
- package/dist/lib/tools/codex-limits.d.ts.map +1 -1
- package/dist/lib/tools/codex-limits.js +39 -340
- package/dist/lib/tools/codex-limits.js.map +1 -1
- package/dist/lib/tools/codex-switch.d.ts.map +1 -1
- package/dist/lib/tools/codex-switch.js +9 -0
- package/dist/lib/tools/codex-switch.js.map +1 -1
- package/dist/lib/tui-quota-cache.d.ts +33 -0
- package/dist/lib/tui-quota-cache.d.ts.map +1 -0
- package/dist/lib/tui-quota-cache.js +247 -0
- package/dist/lib/tui-quota-cache.js.map +1 -0
- package/dist/lib/tui-status.d.ts +55 -0
- package/dist/lib/tui-status.d.ts.map +1 -0
- package/dist/lib/tui-status.js +343 -0
- package/dist/lib/tui-status.js.map +1 -0
- package/dist/lib/types.d.ts +3 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/tui.d.ts +6 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +363 -0
- package/dist/tui.js.map +1 -0
- package/package.json +16 -2
- package/scripts/install-oc-codex-multi-auth-core.js +119 -18
package/dist/lib/auth/server.js
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
2
|
import { logError, logWarn } from "../logger.js";
|
|
3
3
|
import { oauthSuccessHtml } from "../oauth-success.js";
|
|
4
|
-
import {
|
|
4
|
+
import { OAUTH_CALLBACK_BIND_HOSTS, OAUTH_CALLBACK_BIND_URL, OAUTH_CALLBACK_PATH, OAUTH_CALLBACK_PORT, } from "../oauth-constants.js";
|
|
5
|
+
function closeServer(server) {
|
|
6
|
+
try {
|
|
7
|
+
server.close();
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
logError(`Failed to close OAuth server: ${err?.message ?? String(err)}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
5
13
|
/**
|
|
6
|
-
* Start a small local HTTP server that waits for /auth/callback and returns the code
|
|
14
|
+
* Start a small local HTTP server that waits for /auth/callback and returns the code.
|
|
15
|
+
*
|
|
16
|
+
* The public redirect URI uses localhost because that is what the Codex OAuth
|
|
17
|
+
* client registration expects. Bind both concrete loopback interfaces so
|
|
18
|
+
* Windows dual-stack localhost resolution can reach either 127.0.0.1 or ::1.
|
|
19
|
+
*
|
|
7
20
|
* @param options - OAuth state for validation
|
|
8
21
|
* @returns Promise that resolves to server info
|
|
9
22
|
*/
|
|
10
|
-
export function startLocalOAuthServer({ state }) {
|
|
23
|
+
export async function startLocalOAuthServer({ state }) {
|
|
11
24
|
let pollAborted = false;
|
|
12
|
-
|
|
25
|
+
let lastCode;
|
|
26
|
+
const allServers = [];
|
|
27
|
+
const handler = (req, res) => {
|
|
13
28
|
try {
|
|
14
29
|
const url = new URL(req.url || "", "http://localhost");
|
|
15
30
|
if (url.pathname !== OAUTH_CALLBACK_PATH) {
|
|
@@ -34,60 +49,89 @@ export function startLocalOAuthServer({ state }) {
|
|
|
34
49
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
35
50
|
res.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'none'");
|
|
36
51
|
res.end(oauthSuccessHtml);
|
|
37
|
-
|
|
52
|
+
lastCode = code;
|
|
53
|
+
for (const server of allServers) {
|
|
54
|
+
server._lastCode = code;
|
|
55
|
+
}
|
|
38
56
|
}
|
|
39
57
|
catch (err) {
|
|
40
58
|
logError(`Request handler error: ${err?.message ?? String(err)}`);
|
|
41
59
|
res.statusCode = 500;
|
|
42
60
|
res.end("Internal error");
|
|
43
61
|
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
server
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
});
|
|
73
|
-
})
|
|
74
|
-
.on("error", (err) => {
|
|
75
|
-
logError(`Failed to bind ${OAUTH_CALLBACK_BIND_URL} (${err?.code}). Suggest device code or manual URL paste.`);
|
|
76
|
-
resolve({
|
|
77
|
-
port: OAUTH_CALLBACK_PORT,
|
|
78
|
-
ready: false,
|
|
79
|
-
close: () => {
|
|
80
|
-
pollAborted = true;
|
|
81
|
-
try {
|
|
82
|
-
server.close();
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
logError(`Failed to close OAuth server: ${err?.message ?? String(err)}`);
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
waitForCode: () => Promise.resolve(null),
|
|
62
|
+
};
|
|
63
|
+
const bindServer = (host) => {
|
|
64
|
+
const server = http.createServer(handler);
|
|
65
|
+
allServers.push(server);
|
|
66
|
+
server.unref();
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
let settled = false;
|
|
69
|
+
const settle = (value) => {
|
|
70
|
+
if (settled)
|
|
71
|
+
return;
|
|
72
|
+
settled = true;
|
|
73
|
+
resolve(value);
|
|
74
|
+
};
|
|
75
|
+
server
|
|
76
|
+
.on("error", (err) => {
|
|
77
|
+
const message = host === OAUTH_CALLBACK_BIND_HOSTS[0]
|
|
78
|
+
? `Failed to bind ${OAUTH_CALLBACK_BIND_URL} (${err?.code}). Suggest device code or manual URL paste.`
|
|
79
|
+
: `Failed to bind http://[::1]:${OAUTH_CALLBACK_PORT} (${err?.code}); continuing if IPv4 loopback is available.`;
|
|
80
|
+
if (host === OAUTH_CALLBACK_BIND_HOSTS[0]) {
|
|
81
|
+
logError(message);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
logWarn(message);
|
|
85
|
+
}
|
|
86
|
+
settle(null);
|
|
87
|
+
})
|
|
88
|
+
.listen(OAUTH_CALLBACK_PORT, host, () => {
|
|
89
|
+
settle(server);
|
|
89
90
|
});
|
|
90
91
|
});
|
|
91
|
-
}
|
|
92
|
+
};
|
|
93
|
+
const boundServers = (await Promise.all(OAUTH_CALLBACK_BIND_HOSTS.map((host) => bindServer(host)))).filter((server) => server !== null);
|
|
94
|
+
if (boundServers.length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
port: OAUTH_CALLBACK_PORT,
|
|
97
|
+
ready: false,
|
|
98
|
+
close: () => {
|
|
99
|
+
pollAborted = true;
|
|
100
|
+
for (const server of allServers) {
|
|
101
|
+
closeServer(server);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
waitForCode: () => Promise.resolve(null),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
port: OAUTH_CALLBACK_PORT,
|
|
109
|
+
ready: true,
|
|
110
|
+
close: () => {
|
|
111
|
+
pollAborted = true;
|
|
112
|
+
for (const server of boundServers) {
|
|
113
|
+
closeServer(server);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
waitForCode: async () => {
|
|
117
|
+
const POLL_INTERVAL_MS = 100;
|
|
118
|
+
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
119
|
+
const maxIterations = Math.floor(TIMEOUT_MS / POLL_INTERVAL_MS);
|
|
120
|
+
const poll = () => new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
121
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
122
|
+
if (pollAborted)
|
|
123
|
+
return null;
|
|
124
|
+
const serverCode = allServers
|
|
125
|
+
.map((server) => server._lastCode)
|
|
126
|
+
.find((code) => typeof code === "string" && code.length > 0);
|
|
127
|
+
const code = lastCode ?? serverCode;
|
|
128
|
+
if (code)
|
|
129
|
+
return { code };
|
|
130
|
+
await poll();
|
|
131
|
+
}
|
|
132
|
+
logWarn("OAuth poll timeout after 5 minutes");
|
|
133
|
+
return null;
|
|
134
|
+
},
|
|
135
|
+
};
|
|
92
136
|
}
|
|
93
137
|
//# sourceMappingURL=server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../lib/auth/server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EACN,uBAAuB,EACvB,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../lib/auth/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type AccountMetadataV3, type AccountStorageV3 } from "./storage.js";
|
|
2
|
+
export type UsageWindow = {
|
|
3
|
+
used_percent?: number;
|
|
4
|
+
limit_window_seconds?: number;
|
|
5
|
+
reset_at?: number;
|
|
6
|
+
reset_after_seconds?: number;
|
|
7
|
+
} | null;
|
|
8
|
+
export type LimitWindow = {
|
|
9
|
+
usedPercent?: number;
|
|
10
|
+
windowMinutes?: number;
|
|
11
|
+
resetAtMs?: number;
|
|
12
|
+
};
|
|
13
|
+
export type UsageRateLimit = {
|
|
14
|
+
primary_window?: UsageWindow;
|
|
15
|
+
secondary_window?: UsageWindow;
|
|
16
|
+
} | null;
|
|
17
|
+
export type UsageCredits = {
|
|
18
|
+
has_credits?: boolean;
|
|
19
|
+
unlimited?: boolean;
|
|
20
|
+
balance?: string | null;
|
|
21
|
+
} | null;
|
|
22
|
+
export type UsagePayload = {
|
|
23
|
+
plan_type?: string;
|
|
24
|
+
rate_limit?: UsageRateLimit;
|
|
25
|
+
code_review_rate_limit?: UsageRateLimit;
|
|
26
|
+
additional_rate_limits?: Array<{
|
|
27
|
+
limit_name?: string;
|
|
28
|
+
metered_feature?: string;
|
|
29
|
+
rate_limit?: UsageRateLimit;
|
|
30
|
+
}> | null;
|
|
31
|
+
credits?: UsageCredits;
|
|
32
|
+
};
|
|
33
|
+
export type UsageLimitPayload = {
|
|
34
|
+
name: string;
|
|
35
|
+
windowMinutes: number | null;
|
|
36
|
+
usedPercent: number | null;
|
|
37
|
+
leftPercent: number | null;
|
|
38
|
+
resetAtMs: number | null;
|
|
39
|
+
summary: string;
|
|
40
|
+
};
|
|
41
|
+
export type AdditionalUsageLimit = {
|
|
42
|
+
name: string;
|
|
43
|
+
window: LimitWindow;
|
|
44
|
+
};
|
|
45
|
+
export type CodexUsageSummary = {
|
|
46
|
+
planType: string | null;
|
|
47
|
+
credits: string | null;
|
|
48
|
+
primary: LimitWindow;
|
|
49
|
+
secondary: LimitWindow;
|
|
50
|
+
codeReview: LimitWindow;
|
|
51
|
+
additionalLimits: AdditionalUsageLimit[];
|
|
52
|
+
limits: UsageLimitPayload[];
|
|
53
|
+
};
|
|
54
|
+
export type EnsureCodexUsageAccessTokenResult = {
|
|
55
|
+
accessToken: string;
|
|
56
|
+
refreshed: boolean;
|
|
57
|
+
persisted: boolean;
|
|
58
|
+
};
|
|
59
|
+
export type UsageAccountSelection = {
|
|
60
|
+
index: number;
|
|
61
|
+
account: AccountMetadataV3;
|
|
62
|
+
};
|
|
63
|
+
export declare function getUsageLeftPercent(usedPercent: number | undefined): number | undefined;
|
|
64
|
+
export declare function formatUsageWindowLabel(windowMinutes: number | undefined): string;
|
|
65
|
+
export declare function formatUsageReset(resetAtMs: number | undefined): string | undefined;
|
|
66
|
+
export declare function mapUsageWindow(window: UsageWindow): LimitWindow;
|
|
67
|
+
export declare function formatUsageLimitTitle(windowMinutes: number | undefined, fallback?: string): string;
|
|
68
|
+
export declare function formatUsageLimitSummary(window: LimitWindow): string;
|
|
69
|
+
export declare function toUsageLimitPayload(name: string, window: LimitWindow): UsageLimitPayload;
|
|
70
|
+
export declare function formatUsageCredits(credits: UsageCredits): string | undefined;
|
|
71
|
+
export declare function formatAdditionalUsageLimitName(name: string | undefined): string;
|
|
72
|
+
export declare function hasUsageWindow(window: LimitWindow): boolean;
|
|
73
|
+
export declare function parseCodexUsagePayload(payload: UsagePayload): CodexUsageSummary;
|
|
74
|
+
export declare function fetchCodexUsage(params: {
|
|
75
|
+
accountId: string;
|
|
76
|
+
accessToken: string;
|
|
77
|
+
organizationId: string | undefined;
|
|
78
|
+
timeoutMs?: number;
|
|
79
|
+
}): Promise<UsagePayload>;
|
|
80
|
+
export declare function ensureCodexUsageAccessToken(params: {
|
|
81
|
+
storage: AccountStorageV3;
|
|
82
|
+
account: AccountMetadataV3;
|
|
83
|
+
}): Promise<EnsureCodexUsageAccessTokenResult>;
|
|
84
|
+
export declare function deduplicateUsageAccountIndices(storage: AccountStorageV3): number[];
|
|
85
|
+
export declare function resolveCodexUsageActiveAccount(storage: AccountStorageV3): UsageAccountSelection | null;
|
|
86
|
+
export declare function resolveCodexUsageAccountId(params: {
|
|
87
|
+
account: AccountMetadataV3;
|
|
88
|
+
accessToken: string;
|
|
89
|
+
}): string | undefined;
|
|
90
|
+
export declare function createUsageAccountFingerprint(account: AccountMetadataV3): string;
|
|
91
|
+
//# sourceMappingURL=codex-usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-usage.d.ts","sourceRoot":"","sources":["../../lib/codex-usage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { extractAccountId } from "./accounts.js";
|
|
3
|
+
import { getFetchTimeoutMs, loadPluginConfig } from "./config.js";
|
|
4
|
+
import { CODEX_BASE_URL, PLUGIN_NAME } from "./constants.js";
|
|
5
|
+
import { createUsageRequestTimeoutError } from "./error-sentinels.js";
|
|
6
|
+
import { logWarn } from "./logger.js";
|
|
7
|
+
import { queuedRefresh } from "./refresh-queue.js";
|
|
8
|
+
import { createCodexHeaders } from "./request/fetch-helpers.js";
|
|
9
|
+
import { withAccountStorageTransaction, } from "./storage.js";
|
|
10
|
+
const usageErrorBodyMaxChars = 4096;
|
|
11
|
+
export function getUsageLeftPercent(usedPercent) {
|
|
12
|
+
return typeof usedPercent === "number" && Number.isFinite(usedPercent)
|
|
13
|
+
? Math.max(0, Math.min(100, Math.round(100 - usedPercent)))
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
export function formatUsageWindowLabel(windowMinutes) {
|
|
17
|
+
if (!windowMinutes ||
|
|
18
|
+
!Number.isFinite(windowMinutes) ||
|
|
19
|
+
windowMinutes <= 0) {
|
|
20
|
+
return "quota";
|
|
21
|
+
}
|
|
22
|
+
if (windowMinutes % 1440 === 0)
|
|
23
|
+
return `${windowMinutes / 1440}d`;
|
|
24
|
+
if (windowMinutes % 60 === 0)
|
|
25
|
+
return `${windowMinutes / 60}h`;
|
|
26
|
+
return `${windowMinutes}m`;
|
|
27
|
+
}
|
|
28
|
+
export function formatUsageReset(resetAtMs) {
|
|
29
|
+
if (!resetAtMs || !Number.isFinite(resetAtMs) || resetAtMs <= 0) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const date = new Date(resetAtMs);
|
|
33
|
+
if (!Number.isFinite(date.getTime()))
|
|
34
|
+
return undefined;
|
|
35
|
+
const now = new Date();
|
|
36
|
+
const sameDay = now.getFullYear() === date.getFullYear() &&
|
|
37
|
+
now.getMonth() === date.getMonth() &&
|
|
38
|
+
now.getDate() === date.getDate();
|
|
39
|
+
const time = date.toLocaleTimeString(undefined, {
|
|
40
|
+
hour: "2-digit",
|
|
41
|
+
minute: "2-digit",
|
|
42
|
+
hour12: false,
|
|
43
|
+
});
|
|
44
|
+
if (sameDay)
|
|
45
|
+
return time;
|
|
46
|
+
const day = date.toLocaleDateString(undefined, {
|
|
47
|
+
month: "short",
|
|
48
|
+
day: "2-digit",
|
|
49
|
+
});
|
|
50
|
+
return `${time} on ${day}`;
|
|
51
|
+
}
|
|
52
|
+
export function mapUsageWindow(window) {
|
|
53
|
+
if (!window)
|
|
54
|
+
return {};
|
|
55
|
+
return {
|
|
56
|
+
usedPercent: typeof window.used_percent === "number" &&
|
|
57
|
+
Number.isFinite(window.used_percent)
|
|
58
|
+
? window.used_percent
|
|
59
|
+
: undefined,
|
|
60
|
+
windowMinutes: typeof window.limit_window_seconds === "number" &&
|
|
61
|
+
Number.isFinite(window.limit_window_seconds)
|
|
62
|
+
? Math.max(1, Math.ceil(window.limit_window_seconds / 60))
|
|
63
|
+
: undefined,
|
|
64
|
+
resetAtMs: typeof window.reset_at === "number" && window.reset_at > 0
|
|
65
|
+
? window.reset_at * 1000
|
|
66
|
+
: typeof window.reset_after_seconds === "number" &&
|
|
67
|
+
window.reset_after_seconds > 0
|
|
68
|
+
? Date.now() + window.reset_after_seconds * 1000
|
|
69
|
+
: undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function formatUsageLimitTitle(windowMinutes, fallback = "quota") {
|
|
73
|
+
if (windowMinutes === 300)
|
|
74
|
+
return "5h limit";
|
|
75
|
+
if (windowMinutes === 10080)
|
|
76
|
+
return "Weekly limit";
|
|
77
|
+
if (fallback !== "quota")
|
|
78
|
+
return fallback;
|
|
79
|
+
return `${formatUsageWindowLabel(windowMinutes)} limit`;
|
|
80
|
+
}
|
|
81
|
+
export function formatUsageLimitSummary(window) {
|
|
82
|
+
const left = getUsageLeftPercent(window.usedPercent);
|
|
83
|
+
const reset = formatUsageReset(window.resetAtMs);
|
|
84
|
+
if (left !== undefined && reset)
|
|
85
|
+
return `${left}% left (resets ${reset})`;
|
|
86
|
+
if (left !== undefined)
|
|
87
|
+
return `${left}% left`;
|
|
88
|
+
if (reset)
|
|
89
|
+
return `resets ${reset}`;
|
|
90
|
+
return "unavailable";
|
|
91
|
+
}
|
|
92
|
+
export function toUsageLimitPayload(name, window) {
|
|
93
|
+
return {
|
|
94
|
+
name,
|
|
95
|
+
windowMinutes: window.windowMinutes ?? null,
|
|
96
|
+
usedPercent: typeof window.usedPercent === "number" ? window.usedPercent : null,
|
|
97
|
+
leftPercent: getUsageLeftPercent(window.usedPercent) ?? null,
|
|
98
|
+
resetAtMs: window.resetAtMs ?? null,
|
|
99
|
+
summary: formatUsageLimitSummary(window),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function formatUsageCredits(credits) {
|
|
103
|
+
if (!credits)
|
|
104
|
+
return undefined;
|
|
105
|
+
if (credits.unlimited)
|
|
106
|
+
return "unlimited";
|
|
107
|
+
if (typeof credits.balance === "string" && credits.balance.trim()) {
|
|
108
|
+
return credits.balance.trim();
|
|
109
|
+
}
|
|
110
|
+
if (credits.has_credits)
|
|
111
|
+
return "available";
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
export function formatAdditionalUsageLimitName(name) {
|
|
115
|
+
if (!name)
|
|
116
|
+
return "Additional limit";
|
|
117
|
+
if (name === "code_review_rate_limit")
|
|
118
|
+
return "Code review";
|
|
119
|
+
return name
|
|
120
|
+
.replace(/[_-]+/g, " ")
|
|
121
|
+
.replace(/\b\w/g, (match) => match.toUpperCase());
|
|
122
|
+
}
|
|
123
|
+
export function hasUsageWindow(window) {
|
|
124
|
+
return Boolean(window.windowMinutes ||
|
|
125
|
+
typeof window.usedPercent === "number" ||
|
|
126
|
+
window.resetAtMs);
|
|
127
|
+
}
|
|
128
|
+
export function parseCodexUsagePayload(payload) {
|
|
129
|
+
const primary = mapUsageWindow(payload.rate_limit?.primary_window ?? null);
|
|
130
|
+
const secondary = mapUsageWindow(payload.rate_limit?.secondary_window ?? null);
|
|
131
|
+
const codeReviewRateLimit = payload.code_review_rate_limit ??
|
|
132
|
+
payload.additional_rate_limits?.find((entry) => entry.limit_name === "code_review_rate_limit")?.rate_limit ??
|
|
133
|
+
null;
|
|
134
|
+
const codeReview = mapUsageWindow(codeReviewRateLimit?.primary_window ?? null);
|
|
135
|
+
const credits = formatUsageCredits(payload.credits ?? null);
|
|
136
|
+
const additionalLimits = (payload.additional_rate_limits ?? [])
|
|
137
|
+
.filter((entry) => entry.limit_name !== "code_review_rate_limit")
|
|
138
|
+
.map((entry) => ({
|
|
139
|
+
name: formatAdditionalUsageLimitName(entry.limit_name ?? entry.metered_feature),
|
|
140
|
+
window: mapUsageWindow(entry.rate_limit?.primary_window ?? null),
|
|
141
|
+
}));
|
|
142
|
+
const limits = [
|
|
143
|
+
toUsageLimitPayload(formatUsageLimitTitle(primary.windowMinutes), primary),
|
|
144
|
+
toUsageLimitPayload(formatUsageLimitTitle(secondary.windowMinutes), secondary),
|
|
145
|
+
];
|
|
146
|
+
if (hasUsageWindow(codeReview)) {
|
|
147
|
+
limits.push(toUsageLimitPayload("Code review", codeReview));
|
|
148
|
+
}
|
|
149
|
+
for (const limit of additionalLimits) {
|
|
150
|
+
limits.push(toUsageLimitPayload(limit.name, limit.window));
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
planType: payload.plan_type ?? null,
|
|
154
|
+
credits: credits ?? null,
|
|
155
|
+
primary,
|
|
156
|
+
secondary,
|
|
157
|
+
codeReview,
|
|
158
|
+
additionalLimits,
|
|
159
|
+
limits,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function sanitizeUsageErrorMessage(status, bodyText) {
|
|
163
|
+
const normalized = bodyText.replace(/\s+/g, " ").trim();
|
|
164
|
+
const redacted = normalized
|
|
165
|
+
.replace(/Bearer\s+\S+/gi, "Bearer [redacted]")
|
|
166
|
+
.replace(/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g, "[redacted-token]")
|
|
167
|
+
.replace(/\bsk-[A-Za-z0-9][A-Za-z0-9._:-]{19,}\b/gi, "[redacted-token]")
|
|
168
|
+
.replace(/\b[a-f0-9]{40,}\b/gi, "[redacted-token]");
|
|
169
|
+
return redacted ? `HTTP ${status}: ${redacted.slice(0, 200)}` : `HTTP ${status}`;
|
|
170
|
+
}
|
|
171
|
+
function isAbortError(error) {
|
|
172
|
+
return ((error instanceof Error && error.name === "AbortError") ||
|
|
173
|
+
(typeof DOMException !== "undefined" &&
|
|
174
|
+
error instanceof DOMException &&
|
|
175
|
+
error.name === "AbortError"));
|
|
176
|
+
}
|
|
177
|
+
export async function fetchCodexUsage(params) {
|
|
178
|
+
const headers = createCodexHeaders(undefined, params.accountId, params.accessToken, {
|
|
179
|
+
organizationId: params.organizationId,
|
|
180
|
+
});
|
|
181
|
+
headers.set("accept", "application/json");
|
|
182
|
+
const controller = new AbortController();
|
|
183
|
+
const timeout = setTimeout(() => controller.abort(), params.timeoutMs ?? getFetchTimeoutMs(loadPluginConfig()));
|
|
184
|
+
try {
|
|
185
|
+
const response = await fetch(`${CODEX_BASE_URL}/wham/usage`, {
|
|
186
|
+
method: "GET",
|
|
187
|
+
headers,
|
|
188
|
+
signal: controller.signal,
|
|
189
|
+
});
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
let bodyText = "";
|
|
192
|
+
try {
|
|
193
|
+
bodyText = (await response.text()).slice(0, usageErrorBodyMaxChars);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
if (isAbortError(error) || controller.signal.aborted) {
|
|
197
|
+
throw createUsageRequestTimeoutError();
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
if (controller.signal.aborted) {
|
|
202
|
+
throw createUsageRequestTimeoutError();
|
|
203
|
+
}
|
|
204
|
+
throw new Error(sanitizeUsageErrorMessage(response.status, bodyText));
|
|
205
|
+
}
|
|
206
|
+
return (await response.json());
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
if (isAbortError(error)) {
|
|
210
|
+
throw createUsageRequestTimeoutError();
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
clearTimeout(timeout);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function applyRefreshedCredentials(target, result) {
|
|
219
|
+
target.refreshToken = result.refresh;
|
|
220
|
+
target.accessToken = result.access;
|
|
221
|
+
target.expiresAt = result.expires;
|
|
222
|
+
}
|
|
223
|
+
async function persistRefreshedCredentials(params) {
|
|
224
|
+
return await withAccountStorageTransaction(async (current, persist) => {
|
|
225
|
+
const latestStorage = current ??
|
|
226
|
+
{
|
|
227
|
+
version: 3,
|
|
228
|
+
accounts: [],
|
|
229
|
+
activeIndex: 0,
|
|
230
|
+
activeIndexByFamily: {},
|
|
231
|
+
};
|
|
232
|
+
const uniqueMatch = (matches) => matches.length === 1 ? matches[0] : undefined;
|
|
233
|
+
let updated = false;
|
|
234
|
+
if (params.previousRefreshToken) {
|
|
235
|
+
for (const storedAccount of latestStorage.accounts) {
|
|
236
|
+
if (storedAccount.refreshToken === params.previousRefreshToken) {
|
|
237
|
+
applyRefreshedCredentials(storedAccount, params.refreshResult);
|
|
238
|
+
updated = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (!updated) {
|
|
243
|
+
const normalizedOrganizationId = params.organizationId?.trim() ?? "";
|
|
244
|
+
const normalizedEmail = params.email?.trim().toLowerCase();
|
|
245
|
+
const orgScopedMatches = params.accountId
|
|
246
|
+
? latestStorage.accounts.filter((storedAccount) => storedAccount.accountId === params.accountId &&
|
|
247
|
+
(storedAccount.organizationId?.trim() ?? "") ===
|
|
248
|
+
normalizedOrganizationId)
|
|
249
|
+
: [];
|
|
250
|
+
const accountIdMatches = params.accountId
|
|
251
|
+
? latestStorage.accounts.filter((storedAccount) => storedAccount.accountId === params.accountId)
|
|
252
|
+
: [];
|
|
253
|
+
const emailMatches = normalizedEmail && !params.accountId
|
|
254
|
+
? latestStorage.accounts.filter((storedAccount) => storedAccount.email?.trim().toLowerCase() === normalizedEmail)
|
|
255
|
+
: [];
|
|
256
|
+
const fallbackTarget = uniqueMatch(orgScopedMatches) ??
|
|
257
|
+
uniqueMatch(accountIdMatches) ??
|
|
258
|
+
uniqueMatch(emailMatches);
|
|
259
|
+
if (fallbackTarget) {
|
|
260
|
+
applyRefreshedCredentials(fallbackTarget, params.refreshResult);
|
|
261
|
+
updated = true;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (updated) {
|
|
265
|
+
await persist(latestStorage);
|
|
266
|
+
}
|
|
267
|
+
if (!updated) {
|
|
268
|
+
logWarn(`[${PLUGIN_NAME}] persistRefreshedCredentials could not find a matching stored account. Refreshed credentials remain in-memory for this invocation only.`, {
|
|
269
|
+
accountId: params.accountId,
|
|
270
|
+
organizationId: params.organizationId,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return updated;
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
export async function ensureCodexUsageAccessToken(params) {
|
|
277
|
+
let accessToken = params.account.accessToken;
|
|
278
|
+
if (typeof accessToken === "string" &&
|
|
279
|
+
accessToken &&
|
|
280
|
+
typeof params.account.expiresAt === "number" &&
|
|
281
|
+
params.account.expiresAt > Date.now() + 30_000) {
|
|
282
|
+
return { accessToken, refreshed: false, persisted: false };
|
|
283
|
+
}
|
|
284
|
+
const previousRefreshToken = params.account.refreshToken;
|
|
285
|
+
if (!previousRefreshToken) {
|
|
286
|
+
throw new Error("Cannot refresh: account has no refresh token");
|
|
287
|
+
}
|
|
288
|
+
const refreshResult = await queuedRefresh(previousRefreshToken);
|
|
289
|
+
if (refreshResult.type !== "success") {
|
|
290
|
+
throw new Error(refreshResult.message ?? refreshResult.reason);
|
|
291
|
+
}
|
|
292
|
+
let refreshedCount = 0;
|
|
293
|
+
for (const storedAccount of params.storage.accounts) {
|
|
294
|
+
if (storedAccount.refreshToken === previousRefreshToken) {
|
|
295
|
+
applyRefreshedCredentials(storedAccount, refreshResult);
|
|
296
|
+
refreshedCount += 1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (refreshedCount === 0) {
|
|
300
|
+
applyRefreshedCredentials(params.account, refreshResult);
|
|
301
|
+
}
|
|
302
|
+
const persisted = await persistRefreshedCredentials({
|
|
303
|
+
previousRefreshToken,
|
|
304
|
+
accountId: params.account.accountId,
|
|
305
|
+
organizationId: params.account.organizationId,
|
|
306
|
+
email: params.account.email,
|
|
307
|
+
refreshResult,
|
|
308
|
+
});
|
|
309
|
+
accessToken = refreshResult.access;
|
|
310
|
+
return { accessToken, refreshed: true, persisted };
|
|
311
|
+
}
|
|
312
|
+
export function deduplicateUsageAccountIndices(storage) {
|
|
313
|
+
const seenTokens = new Set();
|
|
314
|
+
const uniqueIndices = [];
|
|
315
|
+
for (let i = 0; i < storage.accounts.length; i += 1) {
|
|
316
|
+
const account = storage.accounts[i];
|
|
317
|
+
if (!account)
|
|
318
|
+
continue;
|
|
319
|
+
const refreshToken = typeof account.refreshToken === "string"
|
|
320
|
+
? account.refreshToken.trim()
|
|
321
|
+
: "";
|
|
322
|
+
if (refreshToken && seenTokens.has(refreshToken))
|
|
323
|
+
continue;
|
|
324
|
+
if (refreshToken)
|
|
325
|
+
seenTokens.add(refreshToken);
|
|
326
|
+
uniqueIndices.push(i);
|
|
327
|
+
}
|
|
328
|
+
return uniqueIndices;
|
|
329
|
+
}
|
|
330
|
+
export function resolveCodexUsageActiveAccount(storage) {
|
|
331
|
+
if (storage.accounts.length === 0)
|
|
332
|
+
return null;
|
|
333
|
+
const rawIndex = storage.activeIndexByFamily?.codex ?? storage.activeIndex;
|
|
334
|
+
const numericIndex = typeof rawIndex === "number" && Number.isFinite(rawIndex) ? rawIndex : 0;
|
|
335
|
+
const index = Math.max(0, Math.min(storage.accounts.length - 1, Math.trunc(numericIndex)));
|
|
336
|
+
const activeAccount = storage.accounts[index];
|
|
337
|
+
if (!activeAccount)
|
|
338
|
+
return null;
|
|
339
|
+
const activeLastUsed = typeof activeAccount.lastUsed === "number" && Number.isFinite(activeAccount.lastUsed)
|
|
340
|
+
? activeAccount.lastUsed
|
|
341
|
+
: 0;
|
|
342
|
+
let newestIndex = index;
|
|
343
|
+
let newestLastUsed = activeLastUsed;
|
|
344
|
+
for (let i = 0; i < storage.accounts.length; i += 1) {
|
|
345
|
+
const account = storage.accounts[i];
|
|
346
|
+
const lastUsed = typeof account?.lastUsed === "number" && Number.isFinite(account.lastUsed)
|
|
347
|
+
? account.lastUsed
|
|
348
|
+
: 0;
|
|
349
|
+
if (account && lastUsed > newestLastUsed) {
|
|
350
|
+
newestIndex = i;
|
|
351
|
+
newestLastUsed = lastUsed;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const account = storage.accounts[newestIndex];
|
|
355
|
+
return account ? { index: newestIndex, account } : null;
|
|
356
|
+
}
|
|
357
|
+
export function resolveCodexUsageAccountId(params) {
|
|
358
|
+
return params.account.accountId ?? extractAccountId(params.accessToken);
|
|
359
|
+
}
|
|
360
|
+
export function createUsageAccountFingerprint(account) {
|
|
361
|
+
const fingerprintSource = [
|
|
362
|
+
account.accountId ?? "",
|
|
363
|
+
account.organizationId ?? "",
|
|
364
|
+
account.refreshToken ?? "",
|
|
365
|
+
].join("\0");
|
|
366
|
+
return createHash("sha256").update(fingerprintSource).digest("hex").slice(0, 16);
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=codex-usage.js.map
|