@howaboua/opencode-usage-plugin 0.1.4-dev.0 → 0.1.4-dev.1
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/ui/formatters/copilot.d.ts +7 -0
- package/dist/ui/formatters/copilot.d.ts.map +1 -0
- package/dist/ui/formatters/copilot.js +21 -0
- package/dist/ui/formatters/proxy.d.ts +7 -0
- package/dist/ui/formatters/proxy.d.ts.map +1 -0
- package/dist/ui/formatters/proxy.js +31 -0
- package/dist/ui/formatters/shared.d.ts +10 -0
- package/dist/ui/formatters/shared.d.ts.map +1 -0
- package/dist/ui/formatters/shared.js +63 -0
- package/dist/ui/status.d.ts +1 -0
- package/dist/ui/status.d.ts.map +1 -1
- package/dist/ui/status.js +34 -197
- package/dist/usage/auth/loader.d.ts +21 -0
- package/dist/usage/auth/loader.d.ts.map +1 -0
- package/dist/usage/auth/loader.js +68 -0
- package/dist/usage/fetch.d.ts +4 -10
- package/dist/usage/fetch.d.ts.map +1 -1
- package/dist/usage/fetch.js +58 -190
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats GitHub Copilot usage snapshots.
|
|
3
|
+
* Handles chat and completion quotas with progress bars and reset times.
|
|
4
|
+
*/
|
|
5
|
+
import type { UsageSnapshot } from "../../types";
|
|
6
|
+
export declare function formatCopilotSnapshot(snapshot: UsageSnapshot): string[];
|
|
7
|
+
//# sourceMappingURL=copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/copilot.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAkBvE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats GitHub Copilot usage snapshots.
|
|
3
|
+
* Handles chat and completion quotas with progress bars and reset times.
|
|
4
|
+
*/
|
|
5
|
+
import { formatBar, formatResetSuffixISO, formatMissingSnapshot } from "./shared";
|
|
6
|
+
export function formatCopilotSnapshot(snapshot) {
|
|
7
|
+
const copilot = snapshot.copilotQuota;
|
|
8
|
+
if (!copilot)
|
|
9
|
+
return formatMissingSnapshot(snapshot);
|
|
10
|
+
const lines = ["→ [GITHUB] Copilot"];
|
|
11
|
+
const reset = copilot.resetTime ? formatResetSuffixISO(copilot.resetTime) : "";
|
|
12
|
+
const total = copilot.total === -1 ? "∞" : copilot.total.toString();
|
|
13
|
+
lines.push(` ${"Chat:".padEnd(13)} ${formatBar(copilot.percentRemaining)} ${copilot.used}/${total}${reset}`);
|
|
14
|
+
if (copilot.completionsUsed !== undefined && copilot.completionsTotal !== undefined) {
|
|
15
|
+
const pct = copilot.completionsTotal > 0
|
|
16
|
+
? Math.round((copilot.completionsUsed / copilot.completionsTotal) * 100)
|
|
17
|
+
: 0;
|
|
18
|
+
lines.push(` ${"Completions:".padEnd(13)} ${formatBar(pct)} ${copilot.completionsUsed}/${copilot.completionsTotal}`);
|
|
19
|
+
}
|
|
20
|
+
return lines;
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats Mirrowel Proxy usage snapshots.
|
|
3
|
+
* Handles multi-provider and multi-tier quota reporting with progress bars.
|
|
4
|
+
*/
|
|
5
|
+
import type { UsageSnapshot } from "../../types";
|
|
6
|
+
export declare function formatProxySnapshot(snapshot: UsageSnapshot): string[];
|
|
7
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/proxy.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAcrE"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats Mirrowel Proxy usage snapshots.
|
|
3
|
+
* Handles multi-provider and multi-tier quota reporting with progress bars.
|
|
4
|
+
*/
|
|
5
|
+
import { formatBar, formatResetSuffixISO, formatMissingSnapshot } from "./shared";
|
|
6
|
+
export function formatProxySnapshot(snapshot) {
|
|
7
|
+
const proxy = snapshot.proxyQuota;
|
|
8
|
+
if (!proxy?.providers?.length)
|
|
9
|
+
return formatMissingSnapshot(snapshot);
|
|
10
|
+
const lines = ["→ [Google] Mirrowel Proxy"];
|
|
11
|
+
for (const provider of proxy.providers) {
|
|
12
|
+
const providerLines = formatProxyProvider(provider);
|
|
13
|
+
if (providerLines.length) {
|
|
14
|
+
lines.push("", ` ${provider.name}:`, ...providerLines);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return lines;
|
|
18
|
+
}
|
|
19
|
+
function formatProxyProvider(provider) {
|
|
20
|
+
const lines = [];
|
|
21
|
+
for (const tier of provider.tiers) {
|
|
22
|
+
if (!tier.quotaGroups?.length)
|
|
23
|
+
continue;
|
|
24
|
+
lines.push(` ${tier.tier === "paid" ? "Paid" : "Free"}:`);
|
|
25
|
+
for (const group of tier.quotaGroups) {
|
|
26
|
+
const reset = group.resetTime ? formatResetSuffixISO(group.resetTime) : "";
|
|
27
|
+
lines.push(` ${group.name.padEnd(9)} ${formatBar(group.remainingPct)} ${group.remaining}/${group.max}${reset}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return lines;
|
|
31
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats various usage snapshots (Proxy, Copilot, Codex) into human-readable text.
|
|
3
|
+
* Provides specialized formatting for progress bars, reset times, and missing data states.
|
|
4
|
+
*/
|
|
5
|
+
import type { UsageSnapshot } from "../../types";
|
|
6
|
+
export declare function formatBar(pct: number): string;
|
|
7
|
+
export declare function formatResetSuffix(resetAt: number | null): string;
|
|
8
|
+
export declare function formatResetSuffixISO(iso: string): string;
|
|
9
|
+
export declare function formatMissingSnapshot(snapshot: UsageSnapshot): string[];
|
|
10
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/ui/formatters/shared.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGhE;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKxD;AAWD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,EAAE,CAgBvE"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formats various usage snapshots (Proxy, Copilot, Codex) into human-readable text.
|
|
3
|
+
* Provides specialized formatting for progress bars, reset times, and missing data states.
|
|
4
|
+
*/
|
|
5
|
+
import { platform, homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
export function formatBar(pct) {
|
|
8
|
+
const clamped = Math.max(0, Math.min(100, pct));
|
|
9
|
+
const size = 15;
|
|
10
|
+
const filled = Math.round((clamped / 100) * size);
|
|
11
|
+
return `${"█".repeat(filled)}${"░".repeat(size - filled)}`;
|
|
12
|
+
}
|
|
13
|
+
export function formatResetSuffix(resetAt) {
|
|
14
|
+
if (!resetAt)
|
|
15
|
+
return "";
|
|
16
|
+
return ` (resets in ${formatTimeDelta(resetAt)})`;
|
|
17
|
+
}
|
|
18
|
+
export function formatResetSuffixISO(iso) {
|
|
19
|
+
try {
|
|
20
|
+
const at = Math.floor(new Date(iso).getTime() / 1000);
|
|
21
|
+
return ` (resets in ${formatTimeDelta(at)})`;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function formatTimeDelta(at) {
|
|
28
|
+
const diff = at - Math.floor(Date.now() / 1000);
|
|
29
|
+
if (diff <= 0)
|
|
30
|
+
return "now";
|
|
31
|
+
if (diff < 60)
|
|
32
|
+
return `${diff}s`;
|
|
33
|
+
if (diff < 3600)
|
|
34
|
+
return `${Math.ceil(diff / 60)}m`;
|
|
35
|
+
if (diff < 84400)
|
|
36
|
+
return `${Math.round(diff / 3600)}h`;
|
|
37
|
+
return `${Math.round(diff / 86400)}d`;
|
|
38
|
+
}
|
|
39
|
+
export function formatMissingSnapshot(snapshot) {
|
|
40
|
+
const { provider } = snapshot;
|
|
41
|
+
const configPath = getConfigPath();
|
|
42
|
+
const instructions = {
|
|
43
|
+
codex: "if you dont have codex oauth, please set your usage-config.jsonc to openai: false",
|
|
44
|
+
proxy: "if you are not running Mirrowel's proxy, please set your usage-config.jsonc to proxy: false",
|
|
45
|
+
copilot: "if you are not running GitHub Copilot, please set your usage-config.jsonc to copilot: false"
|
|
46
|
+
};
|
|
47
|
+
const lines = [`→ [${provider.toUpperCase()}] - ${instructions[provider] || ""}`];
|
|
48
|
+
if (snapshot.missingReason)
|
|
49
|
+
lines.push("", `Reason: ${snapshot.missingReason}`);
|
|
50
|
+
if (snapshot.missingDetails?.length) {
|
|
51
|
+
lines.push("", "Details:", ...snapshot.missingDetails.map((d) => `- ${d}`));
|
|
52
|
+
}
|
|
53
|
+
return [...lines, "", `File: ${configPath}`, "", "Issue? https://github.com/IgorWarzocha/opencode-usage-plugin/issues"];
|
|
54
|
+
}
|
|
55
|
+
function getConfigPath() {
|
|
56
|
+
const home = homedir();
|
|
57
|
+
const plat = platform();
|
|
58
|
+
if (plat === "darwin")
|
|
59
|
+
return join(home, ".config", "opencode", "usage-config.jsonc");
|
|
60
|
+
if (plat === "win32")
|
|
61
|
+
return join(process.env.APPDATA || join(home, "AppData", "Roaming"), "opencode", "usage-config.jsonc");
|
|
62
|
+
return join(process.env.XDG_CONFIG_HOME || join(home, ".config"), "opencode", "usage-config.jsonc");
|
|
63
|
+
}
|
package/dist/ui/status.d.ts
CHANGED
package/dist/ui/status.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/ui/status.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAK1C,KAAK,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AAExC,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhB;AAiCD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,GAAG,OAAO,CAAC,IAAI,CAAC,CAahB"}
|
package/dist/ui/status.js
CHANGED
|
@@ -1,231 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Renders usage snapshots into readable status text.
|
|
3
|
+
* Dispatches to specialized formatters and manages session messaging.
|
|
3
4
|
*/
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { formatProxySnapshot } from "./formatters/proxy";
|
|
6
|
+
import { formatCopilotSnapshot } from "./formatters/copilot";
|
|
7
|
+
import { formatBar, formatResetSuffix, formatMissingSnapshot } from "./formatters/shared";
|
|
6
8
|
export async function sendStatusMessage(options) {
|
|
7
|
-
// @ts-ignore
|
|
8
9
|
const bus = options.client.bus;
|
|
9
10
|
if (bus) {
|
|
10
11
|
try {
|
|
11
12
|
await bus.publish({
|
|
12
13
|
topic: "companion.projection",
|
|
13
|
-
body: {
|
|
14
|
-
key: "usage",
|
|
15
|
-
kind: "markdown",
|
|
16
|
-
content: options.text,
|
|
17
|
-
},
|
|
14
|
+
body: { key: "usage", kind: "markdown", content: options.text },
|
|
18
15
|
});
|
|
19
16
|
}
|
|
20
17
|
catch { }
|
|
21
18
|
}
|
|
22
|
-
await options.client.session
|
|
23
|
-
.prompt({
|
|
19
|
+
await options.client.session.prompt({
|
|
24
20
|
path: { id: options.sessionID },
|
|
25
|
-
body: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
type: "text",
|
|
30
|
-
text: options.text,
|
|
31
|
-
ignored: true,
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
|
-
},
|
|
35
|
-
})
|
|
36
|
-
.catch(async () => {
|
|
37
|
-
await options.client.tui
|
|
38
|
-
.showToast({
|
|
21
|
+
body: { noReply: true, parts: [{ type: "text", text: options.text, ignored: true }] },
|
|
22
|
+
}).catch(async () => {
|
|
23
|
+
await options.client.tui.showToast({
|
|
39
24
|
body: { title: "Usage Status", message: options.text, variant: "info" },
|
|
40
|
-
})
|
|
41
|
-
.catch(() => { });
|
|
25
|
+
}).catch(() => { });
|
|
42
26
|
});
|
|
43
27
|
}
|
|
44
|
-
function formatBar(remainingPercent) {
|
|
45
|
-
const clamped = Math.max(0, Math.min(100, remainingPercent));
|
|
46
|
-
const size = 15;
|
|
47
|
-
const filled = Math.round((clamped / 100) * size);
|
|
48
|
-
const empty = size - filled;
|
|
49
|
-
return `${"█".repeat(filled)}${"░".repeat(empty)}`;
|
|
50
|
-
}
|
|
51
|
-
function formatPlanType(planType) {
|
|
52
|
-
return planType
|
|
53
|
-
.replace(/_/g, " ")
|
|
54
|
-
.split(" ")
|
|
55
|
-
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
56
|
-
.join(" ");
|
|
57
|
-
}
|
|
58
|
-
function formatResetTime(resetAt) {
|
|
59
|
-
const now = Math.floor(Date.now() / 1000);
|
|
60
|
-
const diff = resetAt - now;
|
|
61
|
-
if (diff <= 0)
|
|
62
|
-
return "now";
|
|
63
|
-
if (diff < 60)
|
|
64
|
-
return `${diff}s`;
|
|
65
|
-
if (diff < 3600)
|
|
66
|
-
return `${Math.ceil(diff / 60)}m`;
|
|
67
|
-
if (diff < 86400)
|
|
68
|
-
return `${Math.round(diff / 3600)}h`;
|
|
69
|
-
return `${Math.round(diff / 86400)}d`;
|
|
70
|
-
}
|
|
71
|
-
function formatResetSuffix(resetAt) {
|
|
72
|
-
if (!resetAt)
|
|
73
|
-
return "";
|
|
74
|
-
return ` (resets in ${formatResetTime(resetAt)})`;
|
|
75
|
-
}
|
|
76
|
-
function formatResetSuffixISO(isoString) {
|
|
77
|
-
try {
|
|
78
|
-
const resetAt = Math.floor(new Date(isoString).getTime() / 1000);
|
|
79
|
-
return ` (resets in ${formatResetTime(resetAt)})`;
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return "";
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function formatProxySnapshot(snapshot) {
|
|
86
|
-
const proxy = snapshot.proxyQuota;
|
|
87
|
-
if (!proxy || !proxy.providers || proxy.providers.length === 0)
|
|
88
|
-
return ["→ [proxy] No data"];
|
|
89
|
-
const lines = ["→ [Google] Mirrowel Proxy"];
|
|
90
|
-
for (const provider of proxy.providers) {
|
|
91
|
-
const providerLines = [];
|
|
92
|
-
for (const tierInfo of provider.tiers) {
|
|
93
|
-
if (!tierInfo.quotaGroups || tierInfo.quotaGroups.length === 0)
|
|
94
|
-
continue;
|
|
95
|
-
const tierLabel = tierInfo.tier === "paid" ? "Paid" : "Free";
|
|
96
|
-
providerLines.push(` ${tierLabel}:`);
|
|
97
|
-
for (const group of tierInfo.quotaGroups) {
|
|
98
|
-
const resetSuffix = group.resetTime ? formatResetSuffixISO(group.resetTime) : "";
|
|
99
|
-
const label = `${group.name}:`.padEnd(9);
|
|
100
|
-
providerLines.push(` ${label} ${formatBar(group.remainingPct)} ${group.remaining}/${group.max}${resetSuffix}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (providerLines.length > 0) {
|
|
104
|
-
lines.push("");
|
|
105
|
-
lines.push(` ${provider.name}:`);
|
|
106
|
-
for (const line of providerLines)
|
|
107
|
-
lines.push(line);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return lines;
|
|
111
|
-
}
|
|
112
|
-
function formatCopilotSnapshot(snapshot) {
|
|
113
|
-
const copilot = snapshot.copilotQuota;
|
|
114
|
-
if (!copilot)
|
|
115
|
-
return ["→ [copilot] No data"];
|
|
116
|
-
const lines = ["→ [GITHUB] Copilot"];
|
|
117
|
-
const resetSuffix = copilot.resetTime ? formatResetSuffixISO(copilot.resetTime) : "";
|
|
118
|
-
const totalLabel = copilot.total === -1 ? "∞" : copilot.total.toString();
|
|
119
|
-
const chatLabel = "Chat:".padEnd(13);
|
|
120
|
-
const chatRemaining = copilot.used;
|
|
121
|
-
const chatPct = copilot.percentRemaining;
|
|
122
|
-
lines.push(` ${chatLabel} ${formatBar(chatPct)} ${chatRemaining}/${totalLabel}${resetSuffix}`);
|
|
123
|
-
if (copilot.completionsUsed !== undefined && copilot.completionsTotal !== undefined) {
|
|
124
|
-
const compLabel = "Completions:".padEnd(13);
|
|
125
|
-
const compPct = copilot.completionsTotal > 0
|
|
126
|
-
? Math.round((copilot.completionsUsed / copilot.completionsTotal) * 100)
|
|
127
|
-
: 0;
|
|
128
|
-
lines.push(` ${compLabel} ${formatBar(compPct)} ${copilot.completionsUsed}/${copilot.completionsTotal}`);
|
|
129
|
-
}
|
|
130
|
-
return lines;
|
|
131
|
-
}
|
|
132
|
-
function getAppDataPath() {
|
|
133
|
-
const home = homedir();
|
|
134
|
-
const plat = platform();
|
|
135
|
-
if (plat === "darwin")
|
|
136
|
-
return join(home, ".config", "opencode");
|
|
137
|
-
if (plat === "win32")
|
|
138
|
-
return join(process.env.APPDATA || join(home, "AppData", "Roaming"), "opencode");
|
|
139
|
-
return join(process.env.XDG_CONFIG_HOME || join(home, ".config"), "opencode");
|
|
140
|
-
}
|
|
141
|
-
function formatMissingSnapshot(snapshot) {
|
|
142
|
-
const provider = snapshot.provider;
|
|
143
|
-
const configPath = join(getAppDataPath(), "usage-config.jsonc");
|
|
144
|
-
let providerInstruction = "";
|
|
145
|
-
if (provider === "codex") {
|
|
146
|
-
providerInstruction = "if you dont have codex oauth, please set your usage-config.jsonc to openai: false";
|
|
147
|
-
}
|
|
148
|
-
else if (provider === "proxy") {
|
|
149
|
-
providerInstruction = "if you are not running Mirrowel's proxy, please set your usage-config.jsonc to proxy: false";
|
|
150
|
-
}
|
|
151
|
-
else if (provider === "copilot") {
|
|
152
|
-
providerInstruction = "if you are not running GitHub Copilot, please set your usage-config.jsonc to copilot: false";
|
|
153
|
-
}
|
|
154
|
-
const lines = [
|
|
155
|
-
`→ [${provider.toUpperCase()}] - ${providerInstruction}`,
|
|
156
|
-
];
|
|
157
|
-
if (snapshot.missingReason) {
|
|
158
|
-
lines.push("", `Reason: ${snapshot.missingReason}`);
|
|
159
|
-
}
|
|
160
|
-
if (snapshot.missingDetails && snapshot.missingDetails.length > 0) {
|
|
161
|
-
lines.push("", "Details:");
|
|
162
|
-
for (const detail of snapshot.missingDetails) {
|
|
163
|
-
lines.push(`- ${detail}`);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
lines.push("", `The file can be found in ${configPath}. Please read the comments in the file or visit the repo for the readme.`, "", "If you are seeing empty usages or errors, despite having everything set up correctly, please fire an issue at https://github.com/IgorWarzocha/opencode-usage-plugin/issues - thank you!");
|
|
167
|
-
return lines;
|
|
168
|
-
}
|
|
169
28
|
function formatSnapshot(snapshot) {
|
|
170
|
-
if (snapshot.isMissing)
|
|
29
|
+
if (snapshot.isMissing)
|
|
171
30
|
return formatMissingSnapshot(snapshot);
|
|
172
|
-
|
|
173
|
-
if (snapshot.provider === "proxy" && snapshot.proxyQuota) {
|
|
31
|
+
if (snapshot.provider === "proxy")
|
|
174
32
|
return formatProxySnapshot(snapshot);
|
|
175
|
-
|
|
176
|
-
if (snapshot.provider === "copilot" && snapshot.copilotQuota) {
|
|
33
|
+
if (snapshot.provider === "copilot")
|
|
177
34
|
return formatCopilotSnapshot(snapshot);
|
|
178
|
-
}
|
|
179
|
-
const plan = snapshot.planType ? ` (${formatPlanType(snapshot.planType)})` : "";
|
|
35
|
+
const plan = snapshot.planType ? ` (${snapshot.planType.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase())})` : "";
|
|
180
36
|
const lines = [`→ [${snapshot.provider.toUpperCase()}]${plan}`];
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const codeReview = snapshot.codeReview;
|
|
194
|
-
if (codeReview) {
|
|
195
|
-
const remainingPct = 100 - codeReview.usedPercent;
|
|
196
|
-
const label = "Code Review:".padEnd(13);
|
|
197
|
-
lines.push(` ${label} ${formatBar(remainingPct)} ${remainingPct.toFixed(0)}% left${formatResetSuffix(codeReview.resetsAt)}`);
|
|
37
|
+
const metrics = [
|
|
38
|
+
{ label: "Hourly:", data: snapshot.primary },
|
|
39
|
+
{ label: "Weekly:", data: snapshot.secondary },
|
|
40
|
+
{ label: "Code Review:", data: snapshot.codeReview }
|
|
41
|
+
];
|
|
42
|
+
let hasData = false;
|
|
43
|
+
for (const m of metrics) {
|
|
44
|
+
if (m.data) {
|
|
45
|
+
const pct = 100 - m.data.usedPercent;
|
|
46
|
+
lines.push(` ${m.label.padEnd(13)} ${formatBar(pct)} ${pct.toFixed(0)}% left${formatResetSuffix(m.data.resetsAt)}`);
|
|
47
|
+
hasData = true;
|
|
48
|
+
}
|
|
198
49
|
}
|
|
199
50
|
if (snapshot.credits?.hasCredits) {
|
|
200
51
|
lines.push(` Credits: ${snapshot.credits.balance}`);
|
|
52
|
+
hasData = true;
|
|
201
53
|
}
|
|
202
|
-
return lines;
|
|
54
|
+
return hasData ? lines : formatMissingSnapshot(snapshot);
|
|
203
55
|
}
|
|
204
56
|
export async function renderUsageStatus(options) {
|
|
205
57
|
if (options.snapshots.length === 0) {
|
|
206
58
|
const filterMsg = options.filter ? ` for "${options.filter}"` : "";
|
|
207
|
-
|
|
208
|
-
client: options.client,
|
|
209
|
-
state: options.state,
|
|
210
|
-
sessionID: options.sessionID,
|
|
211
|
-
text: `▣ Usage | No data received${filterMsg}.`,
|
|
212
|
-
});
|
|
213
|
-
return;
|
|
59
|
+
return sendStatusMessage({ ...options, text: `▣ Usage | No data received${filterMsg}.` });
|
|
214
60
|
}
|
|
215
61
|
const lines = ["▣ Usage Status", ""];
|
|
216
|
-
options.snapshots.forEach((
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
lines.push(
|
|
220
|
-
if (index < options.snapshots.length - 1) {
|
|
221
|
-
lines.push("");
|
|
222
|
-
lines.push("---");
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
await sendStatusMessage({
|
|
226
|
-
client: options.client,
|
|
227
|
-
state: options.state,
|
|
228
|
-
sessionID: options.sessionID,
|
|
229
|
-
text: lines.join("\n"),
|
|
62
|
+
options.snapshots.forEach((s, i) => {
|
|
63
|
+
lines.push(...formatSnapshot(s));
|
|
64
|
+
if (i < options.snapshots.length - 1)
|
|
65
|
+
lines.push("", "---");
|
|
230
66
|
});
|
|
67
|
+
await sendStatusMessage({ ...options, text: lines.join("\n") });
|
|
231
68
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for loading and merging authentication records from multiple system paths.
|
|
3
|
+
* Handles platform-specific path resolution and specific provider auth transformations.
|
|
4
|
+
*/
|
|
5
|
+
import z from "zod";
|
|
6
|
+
declare const authEntrySchema: z.ZodObject<{
|
|
7
|
+
type: z.ZodOptional<z.ZodString>;
|
|
8
|
+
access: z.ZodOptional<z.ZodString>;
|
|
9
|
+
refresh: z.ZodOptional<z.ZodString>;
|
|
10
|
+
enterpriseUrl: z.ZodOptional<z.ZodString>;
|
|
11
|
+
accountId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
key: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$loose>;
|
|
14
|
+
export type AuthEntry = z.infer<typeof authEntrySchema>;
|
|
15
|
+
export type AuthRecord = Record<string, AuthEntry>;
|
|
16
|
+
export declare function loadMergedAuths(): Promise<{
|
|
17
|
+
auths: AuthRecord;
|
|
18
|
+
codexDiagnostics: string[];
|
|
19
|
+
}>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/usage/auth/loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,CAAC,MAAM,KAAK,CAAA;AAGnB,QAAA,MAAM,eAAe;;;;;;;iBAOL,CAAA;AAIhB,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AACvD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAElD,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,KAAK,EAAE,UAAU,CAAA;IACjB,gBAAgB,EAAE,MAAM,EAAE,CAAA;CAC3B,CAAC,CAYD"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility for loading and merging authentication records from multiple system paths.
|
|
3
|
+
* Handles platform-specific path resolution and specific provider auth transformations.
|
|
4
|
+
*/
|
|
5
|
+
import z from "zod";
|
|
6
|
+
import { getPossibleAuthPaths } from "../../utils";
|
|
7
|
+
const authEntrySchema = z.object({
|
|
8
|
+
type: z.string().optional(),
|
|
9
|
+
access: z.string().optional(),
|
|
10
|
+
refresh: z.string().optional(),
|
|
11
|
+
enterpriseUrl: z.string().optional(),
|
|
12
|
+
accountId: z.string().optional(),
|
|
13
|
+
key: z.string().optional(),
|
|
14
|
+
}).passthrough();
|
|
15
|
+
const authRecordSchema = z.record(z.string(), authEntrySchema);
|
|
16
|
+
export async function loadMergedAuths() {
|
|
17
|
+
const possiblePaths = getPossibleAuthPaths();
|
|
18
|
+
const mergedAuth = {};
|
|
19
|
+
const codexDiagnostics = [`Auth paths checked: ${possiblePaths.join(", ")}`];
|
|
20
|
+
const orderedPaths = [...possiblePaths].reverse();
|
|
21
|
+
for (const authPath of orderedPaths) {
|
|
22
|
+
const diagnostics = await processAuthPath(authPath, mergedAuth);
|
|
23
|
+
codexDiagnostics.push(...diagnostics);
|
|
24
|
+
}
|
|
25
|
+
return { auths: mergedAuth, codexDiagnostics };
|
|
26
|
+
}
|
|
27
|
+
async function processAuthPath(authPath, mergedAuth) {
|
|
28
|
+
const diagnostics = [];
|
|
29
|
+
try {
|
|
30
|
+
const file = Bun.file(authPath);
|
|
31
|
+
if (!(await file.exists()))
|
|
32
|
+
return [`Missing auth file: ${authPath}`];
|
|
33
|
+
const data = await file.json();
|
|
34
|
+
if (typeof data !== "object" || data === null)
|
|
35
|
+
return [`Auth file is not a JSON object: ${authPath}`];
|
|
36
|
+
if (authPath.includes(".codex")) {
|
|
37
|
+
const parsed = parseCodexAuth(data, authPath);
|
|
38
|
+
if (parsed.auth)
|
|
39
|
+
Object.assign(mergedAuth, parsed.auth);
|
|
40
|
+
return parsed.diagnostics;
|
|
41
|
+
}
|
|
42
|
+
const parsed = authRecordSchema.safeParse(data);
|
|
43
|
+
if (!parsed.success)
|
|
44
|
+
return [`Auth file failed schema validation: ${authPath}`];
|
|
45
|
+
Object.assign(mergedAuth, parsed.data);
|
|
46
|
+
diagnostics.push(`Loaded auth from ${authPath}`);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
diagnostics.push(`Failed to read auth file ${authPath}: ${e.message}`);
|
|
50
|
+
}
|
|
51
|
+
return diagnostics;
|
|
52
|
+
}
|
|
53
|
+
function parseCodexAuth(data, path) {
|
|
54
|
+
const tokens = data.tokens;
|
|
55
|
+
if (!tokens?.access_token)
|
|
56
|
+
return { auth: null, diagnostics: [`Invalid Codex auth in ${path}`] };
|
|
57
|
+
return {
|
|
58
|
+
auth: {
|
|
59
|
+
openai: {
|
|
60
|
+
type: "oauth",
|
|
61
|
+
access: tokens.access_token,
|
|
62
|
+
accountId: tokens.account_id,
|
|
63
|
+
refresh: tokens.refresh_token,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
diagnostics: [`Codex CLI auth loaded from ${path}`],
|
|
67
|
+
};
|
|
68
|
+
}
|
package/dist/usage/fetch.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Orchestrates the fetching of usage snapshots from multiple providers.
|
|
3
|
+
* Manages provider filtering, concurrency, and fallback for missing data.
|
|
4
4
|
*/
|
|
5
5
|
import type { UsageSnapshot } from "../types";
|
|
6
|
-
import type { AuthRecord } from "./registry";
|
|
7
|
-
export declare const providerAliases: Record<string, string>;
|
|
8
|
-
export declare function resolveProviderFilter(filter?: string): string | undefined;
|
|
9
6
|
export declare function fetchUsageSnapshots(filter?: string): Promise<UsageSnapshot[]>;
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
auths: AuthRecord;
|
|
13
|
-
codexDiagnostics: string[];
|
|
14
|
-
}>;
|
|
7
|
+
export declare function resolveProviderFilter(filter?: string): string | undefined;
|
|
8
|
+
export declare function loadAuths(): Promise<import("./auth/loader").AuthRecord>;
|
|
15
9
|
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/usage/fetch.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAQ7C,wBAAsB,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAoCnF;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEzE;AA6BD,wBAAsB,SAAS,gDAG9B"}
|
package/dist/usage/fetch.js
CHANGED
|
@@ -1,215 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Orchestrates the fetching of usage snapshots from multiple providers.
|
|
3
|
+
* Manages provider filtering, concurrency, and fallback for missing data.
|
|
4
4
|
*/
|
|
5
|
-
import z from "zod";
|
|
6
5
|
import { providers } from "../providers";
|
|
7
6
|
import { loadUsageConfig } from "./config";
|
|
8
|
-
import {
|
|
7
|
+
import { loadMergedAuths } from "./auth/loader";
|
|
9
8
|
import { resolveProviderAuths } from "./registry";
|
|
10
|
-
const
|
|
11
|
-
.object({
|
|
12
|
-
type: z.string().optional(),
|
|
13
|
-
access: z.string().optional(),
|
|
14
|
-
refresh: z.string().optional(),
|
|
15
|
-
enterpriseUrl: z.string().optional(),
|
|
16
|
-
accountId: z.string().optional(),
|
|
17
|
-
key: z.string().optional(),
|
|
18
|
-
})
|
|
19
|
-
.passthrough();
|
|
20
|
-
const authRecordSchema = z.record(z.string(), authEntrySchema);
|
|
21
|
-
export const providerAliases = {
|
|
22
|
-
codex: "codex",
|
|
23
|
-
openai: "codex",
|
|
24
|
-
gpt: "codex",
|
|
25
|
-
proxy: "proxy",
|
|
26
|
-
agy: "proxy",
|
|
27
|
-
antigravity: "proxy",
|
|
28
|
-
gemini: "proxy",
|
|
29
|
-
copilot: "copilot",
|
|
30
|
-
gh: "copilot",
|
|
31
|
-
github: "copilot",
|
|
32
|
-
};
|
|
33
|
-
export function resolveProviderFilter(filter) {
|
|
34
|
-
if (!filter)
|
|
35
|
-
return undefined;
|
|
36
|
-
const normalized = filter.toLowerCase().trim();
|
|
37
|
-
return providerAliases[normalized];
|
|
38
|
-
}
|
|
9
|
+
const CORE_PROVIDERS = ["codex", "proxy", "copilot"];
|
|
39
10
|
export async function fetchUsageSnapshots(filter) {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
11
|
+
const target = resolveFilter(filter);
|
|
12
|
+
const config = await loadUsageConfig().catch(() => null);
|
|
13
|
+
const toggles = config?.providers ?? {};
|
|
14
|
+
const isEnabled = (id) => {
|
|
44
15
|
if (id === "codex")
|
|
45
|
-
return
|
|
46
|
-
|
|
47
|
-
return providerToggles.proxy !== false;
|
|
48
|
-
if (id === "copilot")
|
|
49
|
-
return providerToggles.copilot !== false;
|
|
50
|
-
return true;
|
|
16
|
+
return toggles.openai !== false;
|
|
17
|
+
return toggles[id] !== false;
|
|
51
18
|
};
|
|
52
|
-
const { auths, codexDiagnostics } = await
|
|
19
|
+
const { auths, codexDiagnostics } = await loadMergedAuths();
|
|
53
20
|
const entries = resolveProviderAuths(auths, null);
|
|
54
21
|
const snapshots = [];
|
|
55
|
-
const
|
|
56
|
-
const fetchedProviders = new Set();
|
|
22
|
+
const fetched = new Set();
|
|
57
23
|
const fetches = entries
|
|
58
|
-
.filter(
|
|
59
|
-
.
|
|
60
|
-
.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const snapshot = await provider.fetchUsage(entry.auth);
|
|
66
|
-
if (snapshot) {
|
|
67
|
-
snapshots.push(snapshot);
|
|
68
|
-
fetchedProviders.add(entry.providerID);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
24
|
+
.filter(e => (!target || e.providerID === target) && isEnabled(e.providerID))
|
|
25
|
+
.map(async (e) => {
|
|
26
|
+
const snap = await providers[e.providerID]?.fetchUsage?.(e.auth).catch(() => null);
|
|
27
|
+
if (snap) {
|
|
28
|
+
snapshots.push(snap);
|
|
29
|
+
fetched.add(e.providerID);
|
|
72
30
|
}
|
|
73
31
|
});
|
|
74
|
-
|
|
75
|
-
for (const id of
|
|
76
|
-
if ((!
|
|
32
|
+
// Handle special/default fetches
|
|
33
|
+
for (const id of ["proxy", "copilot"]) {
|
|
34
|
+
if ((!target || target === id) && isEnabled(id) && !fetched.has(id)) {
|
|
77
35
|
const provider = providers[id];
|
|
78
36
|
if (provider?.fetchUsage) {
|
|
79
|
-
fetches.push(provider
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
snapshots.push(snapshot);
|
|
84
|
-
fetchedProviders.add(id);
|
|
37
|
+
fetches.push(provider.fetchUsage(undefined).then(s => {
|
|
38
|
+
if (s) {
|
|
39
|
+
snapshots.push(s);
|
|
40
|
+
fetched.add(id);
|
|
85
41
|
}
|
|
86
|
-
})
|
|
87
|
-
.catch(() => { }));
|
|
42
|
+
}).catch(() => { }));
|
|
88
43
|
}
|
|
89
44
|
}
|
|
90
45
|
}
|
|
91
|
-
await Promise.race([Promise.all(fetches),
|
|
92
|
-
|
|
93
|
-
if (isProviderEnabled(id) && !fetchedProviders.has(id)) {
|
|
94
|
-
if (!targetProvider || targetProvider === id) {
|
|
95
|
-
const codexDetails = id === "codex" && codexDiagnostics.length > 0 ? codexDiagnostics : undefined;
|
|
96
|
-
snapshots.push({
|
|
97
|
-
timestamp: Date.now(),
|
|
98
|
-
provider: id,
|
|
99
|
-
planType: null,
|
|
100
|
-
primary: null,
|
|
101
|
-
secondary: null,
|
|
102
|
-
codeReview: null,
|
|
103
|
-
credits: null,
|
|
104
|
-
updatedAt: Date.now(),
|
|
105
|
-
isMissing: true,
|
|
106
|
-
missingReason: codexDetails ? "Codex auth was not resolved from auth.json." : undefined,
|
|
107
|
-
missingDetails: codexDetails,
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return snapshots;
|
|
46
|
+
await Promise.race([Promise.all(fetches), new Promise(r => setTimeout(r, 5000))]);
|
|
47
|
+
return appendMissingStates(snapshots, fetched, isEnabled, target, codexDiagnostics);
|
|
113
48
|
}
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
49
|
+
function resolveFilter(f) {
|
|
50
|
+
const aliases = {
|
|
51
|
+
codex: "codex", openai: "codex", gpt: "codex",
|
|
52
|
+
proxy: "proxy", agy: "proxy", gemini: "proxy",
|
|
53
|
+
copilot: "copilot", github: "copilot"
|
|
54
|
+
};
|
|
55
|
+
return f ? aliases[f.toLowerCase().trim()] : undefined;
|
|
117
56
|
}
|
|
118
|
-
export
|
|
119
|
-
|
|
120
|
-
const mergedAuth = {};
|
|
121
|
-
const codexDiagnostics = [];
|
|
122
|
-
const orderedPaths = [...possiblePaths].reverse();
|
|
123
|
-
codexDiagnostics.push(`Auth paths checked: ${possiblePaths.join(", ")}`);
|
|
124
|
-
for (const authPath of orderedPaths) {
|
|
125
|
-
try {
|
|
126
|
-
const file = Bun.file(authPath);
|
|
127
|
-
const exists = await file.exists();
|
|
128
|
-
if (!exists) {
|
|
129
|
-
codexDiagnostics.push(`Missing auth file: ${authPath}`);
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
const data = await file.json();
|
|
133
|
-
if (!isRecord(data)) {
|
|
134
|
-
codexDiagnostics.push(`Auth file is not a JSON object: ${authPath}`);
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
if (authPath.includes(".codex")) {
|
|
138
|
-
const parsed = parseCodexAuth(data, authPath);
|
|
139
|
-
codexDiagnostics.push(...parsed.diagnostics);
|
|
140
|
-
if (parsed.auth)
|
|
141
|
-
Object.assign(mergedAuth, parsed.auth);
|
|
142
|
-
continue;
|
|
143
|
-
}
|
|
144
|
-
const parsed = authRecordSchema.safeParse(data);
|
|
145
|
-
if (!parsed.success) {
|
|
146
|
-
codexDiagnostics.push(`Auth file failed schema validation: ${authPath}`);
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
Object.assign(mergedAuth, parsed.data);
|
|
150
|
-
const openaiEntry = parsed.data.openai ?? parsed.data.codex;
|
|
151
|
-
if (!openaiEntry) {
|
|
152
|
-
codexDiagnostics.push(`No "openai" or "codex" entry in auth file: ${authPath}`);
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
codexDiagnostics.push(`Found OpenAI/Codex entry in auth file: ${authPath}`);
|
|
156
|
-
if (openaiEntry.type && openaiEntry.type !== "oauth" && openaiEntry.type !== "token") {
|
|
157
|
-
codexDiagnostics.push(`OpenAI/Codex entry has unsupported type "${openaiEntry.type}" in ${authPath}`);
|
|
158
|
-
}
|
|
159
|
-
if (!openaiEntry.access && !openaiEntry.key) {
|
|
160
|
-
codexDiagnostics.push(`OpenAI/Codex entry missing "access" or "key" in ${authPath}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
165
|
-
codexDiagnostics.push(`Failed to read auth file ${authPath}: ${message}`);
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
const mergedEntry = mergedAuth.openai ?? mergedAuth.codex;
|
|
170
|
-
if (!mergedEntry) {
|
|
171
|
-
codexDiagnostics.push("Merged auth record has no OpenAI/Codex entry.");
|
|
172
|
-
}
|
|
173
|
-
else {
|
|
174
|
-
if (mergedEntry.type && mergedEntry.type !== "oauth" && mergedEntry.type !== "token") {
|
|
175
|
-
codexDiagnostics.push(`Merged OpenAI/Codex entry has unsupported type "${mergedEntry.type}".`);
|
|
176
|
-
}
|
|
177
|
-
if (!mergedEntry.access && !mergedEntry.key) {
|
|
178
|
-
codexDiagnostics.push("Merged OpenAI/Codex entry missing access or key.");
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return { auths: mergedAuth, codexDiagnostics };
|
|
57
|
+
export function resolveProviderFilter(filter) {
|
|
58
|
+
return resolveFilter(filter);
|
|
182
59
|
}
|
|
183
|
-
function
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
60
|
+
function appendMissingStates(snaps, fetched, isEnabled, target, diagnostics) {
|
|
61
|
+
for (const id of CORE_PROVIDERS) {
|
|
62
|
+
if (isEnabled(id) && !fetched.has(id) && (!target || target === id)) {
|
|
63
|
+
snaps.push({
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
provider: id,
|
|
66
|
+
planType: null,
|
|
67
|
+
primary: null,
|
|
68
|
+
secondary: null,
|
|
69
|
+
codeReview: null,
|
|
70
|
+
credits: null,
|
|
71
|
+
updatedAt: Date.now(),
|
|
72
|
+
isMissing: true,
|
|
73
|
+
missingReason: id === "codex" ? "Auth resolution failed" : undefined,
|
|
74
|
+
missingDetails: id === "codex" ? diagnostics : undefined
|
|
75
|
+
});
|
|
76
|
+
}
|
|
194
77
|
}
|
|
195
|
-
|
|
196
|
-
const refreshToken = tokens.refresh_token;
|
|
197
|
-
diagnostics.push(`Codex CLI auth loaded from ${authPath}`);
|
|
198
|
-
return {
|
|
199
|
-
auth: {
|
|
200
|
-
openai: {
|
|
201
|
-
type: "oauth",
|
|
202
|
-
access: accessToken,
|
|
203
|
-
accountId: typeof accountId === "string" ? accountId : undefined,
|
|
204
|
-
refresh: typeof refreshToken === "string" ? refreshToken : undefined,
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
diagnostics,
|
|
208
|
-
};
|
|
78
|
+
return snaps;
|
|
209
79
|
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
function timeout(ms) {
|
|
214
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
80
|
+
export async function loadAuths() {
|
|
81
|
+
const { auths } = await loadMergedAuths();
|
|
82
|
+
return auths;
|
|
215
83
|
}
|
package/package.json
CHANGED