@ekworp/opencode-mystatus 0.0.0-stage → 1.2.7
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/LICENSE +21 -0
- package/README.md +227 -2
- package/README.zh-CN.md +226 -0
- package/command/mystatus.md +5 -0
- package/dist/plugin/lib/copilot.d.ts +21 -0
- package/dist/plugin/lib/copilot.d.ts.map +1 -0
- package/dist/plugin/lib/copilot.js +346 -0
- package/dist/plugin/lib/google.d.ts +15 -0
- package/dist/plugin/lib/google.d.ts.map +1 -0
- package/dist/plugin/lib/google.js +265 -0
- package/dist/plugin/lib/i18n.d.ts +102 -0
- package/dist/plugin/lib/i18n.d.ts.map +1 -0
- package/dist/plugin/lib/i18n.js +166 -0
- package/dist/plugin/lib/openai.d.ts +17 -0
- package/dist/plugin/lib/openai.d.ts.map +1 -0
- package/dist/plugin/lib/openai.js +161 -0
- package/dist/plugin/lib/types.d.ts +92 -0
- package/dist/plugin/lib/types.d.ts.map +1 -0
- package/dist/plugin/lib/types.js +13 -0
- package/dist/plugin/lib/utils.d.ts +44 -0
- package/dist/plugin/lib/utils.d.ts.map +1 -0
- package/dist/plugin/lib/utils.js +115 -0
- package/dist/plugin/lib/zhipu.d.ts +23 -0
- package/dist/plugin/lib/zhipu.d.ts.map +1 -0
- package/dist/plugin/lib/zhipu.js +134 -0
- package/dist/plugin/mystatus.d.ts +12 -0
- package/dist/plugin/mystatus.d.ts.map +1 -0
- package/dist/plugin/mystatus.js +100 -0
- package/package.json +62 -3
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 国际化模块
|
|
3
|
+
*
|
|
4
|
+
* [输入]: 系统语言环境
|
|
5
|
+
* [输出]: 翻译函数和当前语言
|
|
6
|
+
* [定位]: 被所有平台模块共享使用
|
|
7
|
+
* [同步]: openai.ts, zhipu.ts, mystatus.ts, utils.ts
|
|
8
|
+
*/
|
|
9
|
+
export type Language = "zh" | "en";
|
|
10
|
+
/** 当前语言(模块加载时检测一次) */
|
|
11
|
+
export declare const currentLang: Language;
|
|
12
|
+
/** 翻译函数 */
|
|
13
|
+
export declare const t: {
|
|
14
|
+
readonly days: (n: number) => string;
|
|
15
|
+
readonly hours: (n: number) => string;
|
|
16
|
+
readonly minutes: (n: number) => string;
|
|
17
|
+
readonly hourLimit: (h: number) => string;
|
|
18
|
+
readonly dayLimit: (d: number) => string;
|
|
19
|
+
readonly remaining: (p: number) => string;
|
|
20
|
+
readonly resetIn: (t: string) => string;
|
|
21
|
+
readonly limitReached: "⚠️ 已达到限额上限!";
|
|
22
|
+
readonly account: "Account:";
|
|
23
|
+
readonly unknown: "未知";
|
|
24
|
+
readonly used: "已用";
|
|
25
|
+
readonly authError: (path: string, err: string) => string;
|
|
26
|
+
readonly apiError: (status: number, text: string) => string;
|
|
27
|
+
readonly timeoutError: (seconds: number) => string;
|
|
28
|
+
readonly tokenExpired: "⚠️ OAuth 授权已过期,请在 OpenCode 中使用一次 OpenAI 模型以刷新授权。";
|
|
29
|
+
readonly noAccounts: "未找到任何已配置的账号。\n\n支持的账号类型:\n- OpenAI (Plus/Team/Pro 订阅用户)\n- 智谱 AI (Coding Plan)\n- Z.ai (Coding Plan)\n- Google Cloud (Antigravity)";
|
|
30
|
+
readonly queryFailed: "❌ 查询失败的账号:\n";
|
|
31
|
+
readonly openaiTitle: "## OpenAI 账号额度";
|
|
32
|
+
readonly zhipuTitle: "## 智谱 AI 账号额度";
|
|
33
|
+
readonly zaiTitle: "## Z.ai 账号额度";
|
|
34
|
+
readonly zhipuApiError: (status: number, text: string) => string;
|
|
35
|
+
readonly zaiApiError: (status: number, text: string) => string;
|
|
36
|
+
readonly zhipuTokensLimit: "5 小时 Token 限额";
|
|
37
|
+
readonly zhipuMcpLimit: "MCP 月度配额";
|
|
38
|
+
readonly zhipuAccountName: "Coding Plan";
|
|
39
|
+
readonly zaiAccountName: "Z.ai";
|
|
40
|
+
readonly noQuotaData: "暂无配额数据";
|
|
41
|
+
readonly googleTitle: "## Google Cloud 账号额度";
|
|
42
|
+
readonly googleApiError: (status: number, text: string) => string;
|
|
43
|
+
readonly googleNoProjectId: "⚠️ 缺少 project_id,无法查询额度。";
|
|
44
|
+
readonly copilotTitle: "## GitHub Copilot 账号额度";
|
|
45
|
+
readonly copilotApiError: (status: number, text: string) => string;
|
|
46
|
+
readonly premiumRequests: "Premium";
|
|
47
|
+
readonly chatQuota: "Chat";
|
|
48
|
+
readonly completionsQuota: "Completions";
|
|
49
|
+
readonly overage: "超额使用";
|
|
50
|
+
readonly overageRequests: "次请求";
|
|
51
|
+
readonly quotaResets: "配额重置";
|
|
52
|
+
readonly resetsSoon: "即将重置";
|
|
53
|
+
readonly modelBreakdown: "模型使用明细:";
|
|
54
|
+
readonly billingPeriod: "计费周期";
|
|
55
|
+
readonly copilotQuotaUnavailable: string;
|
|
56
|
+
readonly copilotQuotaWorkaround: string;
|
|
57
|
+
} | {
|
|
58
|
+
readonly days: (n: number) => string;
|
|
59
|
+
readonly hours: (n: number) => string;
|
|
60
|
+
readonly minutes: (n: number) => string;
|
|
61
|
+
readonly hourLimit: (h: number) => string;
|
|
62
|
+
readonly dayLimit: (d: number) => string;
|
|
63
|
+
readonly remaining: (p: number) => string;
|
|
64
|
+
readonly resetIn: (t: string) => string;
|
|
65
|
+
readonly limitReached: "⚠️ Rate limit reached!";
|
|
66
|
+
readonly account: "Account:";
|
|
67
|
+
readonly unknown: "unknown";
|
|
68
|
+
readonly used: "Used";
|
|
69
|
+
readonly authError: (path: string, err: string) => string;
|
|
70
|
+
readonly apiError: (status: number, text: string) => string;
|
|
71
|
+
readonly timeoutError: (seconds: number) => string;
|
|
72
|
+
readonly tokenExpired: "⚠️ OAuth token expired. Please use an OpenAI model in OpenCode to refresh authorization.";
|
|
73
|
+
readonly noAccounts: "No configured accounts found.\n\nSupported account types:\n- OpenAI (Plus/Team/Pro subscribers)\n- Zhipu AI (Coding Plan)\n- Z.ai (Coding Plan)\n- Google Cloud (Antigravity)";
|
|
74
|
+
readonly queryFailed: "❌ Failed to query accounts:\n";
|
|
75
|
+
readonly openaiTitle: "## OpenAI Account Quota";
|
|
76
|
+
readonly zhipuTitle: "## Zhipu AI Account Quota";
|
|
77
|
+
readonly zaiTitle: "## Z.ai Account Quota";
|
|
78
|
+
readonly zhipuApiError: (status: number, text: string) => string;
|
|
79
|
+
readonly zaiApiError: (status: number, text: string) => string;
|
|
80
|
+
readonly zhipuTokensLimit: "5-hour token limit";
|
|
81
|
+
readonly zhipuMcpLimit: "MCP monthly quota";
|
|
82
|
+
readonly zhipuAccountName: "Coding Plan";
|
|
83
|
+
readonly zaiAccountName: "Z.ai";
|
|
84
|
+
readonly noQuotaData: "No quota data available";
|
|
85
|
+
readonly googleTitle: "## Google Cloud Account Quota";
|
|
86
|
+
readonly googleApiError: (status: number, text: string) => string;
|
|
87
|
+
readonly googleNoProjectId: "⚠️ Missing project_id, cannot query quota.";
|
|
88
|
+
readonly copilotTitle: "## GitHub Copilot Account Quota";
|
|
89
|
+
readonly copilotApiError: (status: number, text: string) => string;
|
|
90
|
+
readonly premiumRequests: "Premium";
|
|
91
|
+
readonly chatQuota: "Chat";
|
|
92
|
+
readonly completionsQuota: "Completions";
|
|
93
|
+
readonly overage: "Overage";
|
|
94
|
+
readonly overageRequests: "requests";
|
|
95
|
+
readonly quotaResets: "Quota resets";
|
|
96
|
+
readonly resetsSoon: "Resets soon";
|
|
97
|
+
readonly modelBreakdown: "Model breakdown:";
|
|
98
|
+
readonly billingPeriod: "Period";
|
|
99
|
+
readonly copilotQuotaUnavailable: string;
|
|
100
|
+
readonly copilotQuotaWorkaround: string;
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../plugin/lib/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;AAmMnC,sBAAsB;AACtB,eAAO,MAAM,WAAW,UAAmB,CAAC;AAE5C,WAAW;AACX,eAAO,MAAM,CAAC;uBApKA,MAAM;wBACL,MAAM;0BACJ,MAAM;4BAGJ,MAAM;2BACP,MAAM;4BACL,MAAM;0BACR,MAAM;;;;;+BASD,MAAM,OAAO,MAAM;gCAElB,MAAM,QAAQ,MAAM;qCAEf,MAAM;;;;;;;qCAaN,MAAM,QAAQ,MAAM;mCAEtB,MAAM,QAAQ,MAAM;;;;;;;sCAUjB,MAAM,QAAQ,MAAM;;;uCAMnB,MAAM,QAAQ,MAAM;;;;;;;;;;;;;uBA0BpC,MAAM;wBACL,MAAM;0BACJ,MAAM;4BAGJ,MAAM;2BACP,MAAM;4BACL,MAAM;0BACR,MAAM;;;;;+BASD,MAAM,OAAO,MAAM;gCAElB,MAAM,QAAQ,MAAM;qCAEf,MAAM;;;;;;;qCAaN,MAAM,QAAQ,MAAM;mCAEtB,MAAM,QAAQ,MAAM;;;;;;;sCAUjB,MAAM,QAAQ,MAAM;;;uCAMnB,MAAM,QAAQ,MAAM;;;;;;;;;;;;CAkCR,CAAC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 国际化模块
|
|
3
|
+
*
|
|
4
|
+
* [输入]: 系统语言环境
|
|
5
|
+
* [输出]: 翻译函数和当前语言
|
|
6
|
+
* [定位]: 被所有平台模块共享使用
|
|
7
|
+
* [同步]: openai.ts, zhipu.ts, mystatus.ts, utils.ts
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// 语言检测
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* 检测用户系统语言
|
|
14
|
+
* 优先使用 Intl API,回退到环境变量,默认英文
|
|
15
|
+
*/
|
|
16
|
+
function detectLanguage() {
|
|
17
|
+
// 1. 优先使用 Intl API(更可靠)
|
|
18
|
+
try {
|
|
19
|
+
const intlLocale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
20
|
+
if (intlLocale.startsWith("zh"))
|
|
21
|
+
return "zh";
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Intl API 不可用,继续尝试环境变量
|
|
25
|
+
}
|
|
26
|
+
// 2. 回退到环境变量
|
|
27
|
+
const lang = process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || "";
|
|
28
|
+
if (lang.startsWith("zh"))
|
|
29
|
+
return "zh";
|
|
30
|
+
// 3. 默认英文
|
|
31
|
+
return "en";
|
|
32
|
+
}
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// 翻译定义
|
|
35
|
+
// ============================================================================
|
|
36
|
+
const translations = {
|
|
37
|
+
zh: {
|
|
38
|
+
// 时间单位
|
|
39
|
+
days: (n) => `${n}天`,
|
|
40
|
+
hours: (n) => `${n}小时`,
|
|
41
|
+
minutes: (n) => `${n}分钟`,
|
|
42
|
+
// 限额相关
|
|
43
|
+
hourLimit: (h) => `${h}小时限额`,
|
|
44
|
+
dayLimit: (d) => `${d}天限额`,
|
|
45
|
+
remaining: (p) => `剩余 ${p}%`,
|
|
46
|
+
resetIn: (t) => `重置: ${t}后`,
|
|
47
|
+
limitReached: "⚠️ 已达到限额上限!",
|
|
48
|
+
// 通用
|
|
49
|
+
account: "Account:",
|
|
50
|
+
unknown: "未知",
|
|
51
|
+
used: "已用",
|
|
52
|
+
// 错误信息
|
|
53
|
+
authError: (path, err) => `❌ 无法读取认证文件: ${path}\n错误: ${err}`,
|
|
54
|
+
apiError: (status, text) => `OpenAI API 请求失败 (${status}): ${text}`,
|
|
55
|
+
timeoutError: (seconds) => `请求超时 (${seconds}秒)`,
|
|
56
|
+
tokenExpired: "⚠️ OAuth 授权已过期,请在 OpenCode 中使用一次 OpenAI 模型以刷新授权。",
|
|
57
|
+
noAccounts: "未找到任何已配置的账号。\n\n支持的账号类型:\n- OpenAI (Plus/Team/Pro 订阅用户)\n- 智谱 AI (Coding Plan)\n- Z.ai (Coding Plan)\n- Google Cloud (Antigravity)",
|
|
58
|
+
queryFailed: "❌ 查询失败的账号:\n",
|
|
59
|
+
// 平台标题
|
|
60
|
+
openaiTitle: "## OpenAI 账号额度",
|
|
61
|
+
zhipuTitle: "## 智谱 AI 账号额度",
|
|
62
|
+
zaiTitle: "## Z.ai 账号额度",
|
|
63
|
+
// 智谱 AI 相关
|
|
64
|
+
zhipuApiError: (status, text) => `智谱 API 请求失败 (${status}): ${text}`,
|
|
65
|
+
zaiApiError: (status, text) => `Z.ai API 请求失败 (${status}): ${text}`,
|
|
66
|
+
zhipuTokensLimit: "5 小时 Token 限额",
|
|
67
|
+
zhipuMcpLimit: "MCP 月度配额",
|
|
68
|
+
zhipuAccountName: "Coding Plan",
|
|
69
|
+
zaiAccountName: "Z.ai",
|
|
70
|
+
noQuotaData: "暂无配额数据",
|
|
71
|
+
// Google 相关
|
|
72
|
+
googleTitle: "## Google Cloud 账号额度",
|
|
73
|
+
googleApiError: (status, text) => `Google API 请求失败 (${status}): ${text}`,
|
|
74
|
+
googleNoProjectId: "⚠️ 缺少 project_id,无法查询额度。",
|
|
75
|
+
// GitHub Copilot 相关
|
|
76
|
+
copilotTitle: "## GitHub Copilot 账号额度",
|
|
77
|
+
copilotApiError: (status, text) => `GitHub Copilot API 请求失败 (${status}): ${text}`,
|
|
78
|
+
premiumRequests: "Premium",
|
|
79
|
+
chatQuota: "Chat",
|
|
80
|
+
completionsQuota: "Completions",
|
|
81
|
+
overage: "超额使用",
|
|
82
|
+
overageRequests: "次请求",
|
|
83
|
+
quotaResets: "配额重置",
|
|
84
|
+
resetsSoon: "即将重置",
|
|
85
|
+
modelBreakdown: "模型使用明细:",
|
|
86
|
+
billingPeriod: "计费周期",
|
|
87
|
+
copilotQuotaUnavailable: "⚠️ GitHub Copilot 配额查询暂时不可用。\n" +
|
|
88
|
+
"OpenCode 的新 OAuth 集成不支持访问配额 API。",
|
|
89
|
+
copilotQuotaWorkaround: "解决方案:\n" +
|
|
90
|
+
"1. 创建一个 fine-grained PAT (访问 https://github.com/settings/tokens?type=beta)\n" +
|
|
91
|
+
"2. 在 'Account permissions' 中将 'Plan' 设为 'Read-only'\n" +
|
|
92
|
+
"3. 创建配置文件 ~/.config/opencode/copilot-quota-token.json:\n" +
|
|
93
|
+
' {"token": "github_pat_xxx...", "username": "你的用户名"}\n\n' +
|
|
94
|
+
"其他方法:\n" +
|
|
95
|
+
"• 在 VS Code 中点击状态栏的 Copilot 图标查看配额\n" +
|
|
96
|
+
"• 访问 https://github.com/settings/billing 查看使用情况",
|
|
97
|
+
},
|
|
98
|
+
en: {
|
|
99
|
+
// 时间单位
|
|
100
|
+
days: (n) => `${n}d`,
|
|
101
|
+
hours: (n) => `${n}h`,
|
|
102
|
+
minutes: (n) => `${n}m`,
|
|
103
|
+
// 限额相关
|
|
104
|
+
hourLimit: (h) => `${h}-hour limit`,
|
|
105
|
+
dayLimit: (d) => `${d}-day limit`,
|
|
106
|
+
remaining: (p) => `${p}% remaining`,
|
|
107
|
+
resetIn: (t) => `Resets in: ${t}`,
|
|
108
|
+
limitReached: "⚠️ Rate limit reached!",
|
|
109
|
+
// 通用
|
|
110
|
+
account: "Account:",
|
|
111
|
+
unknown: "unknown",
|
|
112
|
+
used: "Used",
|
|
113
|
+
// 错误信息
|
|
114
|
+
authError: (path, err) => `❌ Failed to read auth file: ${path}\nError: ${err}`,
|
|
115
|
+
apiError: (status, text) => `OpenAI API request failed (${status}): ${text}`,
|
|
116
|
+
timeoutError: (seconds) => `Request timeout (${seconds}s)`,
|
|
117
|
+
tokenExpired: "⚠️ OAuth token expired. Please use an OpenAI model in OpenCode to refresh authorization.",
|
|
118
|
+
noAccounts: "No configured accounts found.\n\nSupported account types:\n- OpenAI (Plus/Team/Pro subscribers)\n- Zhipu AI (Coding Plan)\n- Z.ai (Coding Plan)\n- Google Cloud (Antigravity)",
|
|
119
|
+
queryFailed: "❌ Failed to query accounts:\n",
|
|
120
|
+
// 平台标题
|
|
121
|
+
openaiTitle: "## OpenAI Account Quota",
|
|
122
|
+
zhipuTitle: "## Zhipu AI Account Quota",
|
|
123
|
+
zaiTitle: "## Z.ai Account Quota",
|
|
124
|
+
// 智谱 AI 相关
|
|
125
|
+
zhipuApiError: (status, text) => `Zhipu API request failed (${status}): ${text}`,
|
|
126
|
+
zaiApiError: (status, text) => `Z.ai API request failed (${status}): ${text}`,
|
|
127
|
+
zhipuTokensLimit: "5-hour token limit",
|
|
128
|
+
zhipuMcpLimit: "MCP monthly quota",
|
|
129
|
+
zhipuAccountName: "Coding Plan",
|
|
130
|
+
zaiAccountName: "Z.ai",
|
|
131
|
+
noQuotaData: "No quota data available",
|
|
132
|
+
// Google 相关
|
|
133
|
+
googleTitle: "## Google Cloud Account Quota",
|
|
134
|
+
googleApiError: (status, text) => `Google API request failed (${status}): ${text}`,
|
|
135
|
+
googleNoProjectId: "⚠️ Missing project_id, cannot query quota.",
|
|
136
|
+
// GitHub Copilot 相关
|
|
137
|
+
copilotTitle: "## GitHub Copilot Account Quota",
|
|
138
|
+
copilotApiError: (status, text) => `GitHub Copilot API request failed (${status}): ${text}`,
|
|
139
|
+
premiumRequests: "Premium",
|
|
140
|
+
chatQuota: "Chat",
|
|
141
|
+
completionsQuota: "Completions",
|
|
142
|
+
overage: "Overage",
|
|
143
|
+
overageRequests: "requests",
|
|
144
|
+
quotaResets: "Quota resets",
|
|
145
|
+
resetsSoon: "Resets soon",
|
|
146
|
+
modelBreakdown: "Model breakdown:",
|
|
147
|
+
billingPeriod: "Period",
|
|
148
|
+
copilotQuotaUnavailable: "⚠️ GitHub Copilot quota query unavailable.\n" +
|
|
149
|
+
"OpenCode's new OAuth integration doesn't support quota API access.",
|
|
150
|
+
copilotQuotaWorkaround: "Solution:\n" +
|
|
151
|
+
"1. Create a fine-grained PAT (visit https://github.com/settings/tokens?type=beta)\n" +
|
|
152
|
+
"2. Under 'Account permissions', set 'Plan' to 'Read-only'\n" +
|
|
153
|
+
"3. Create config file ~/.config/opencode/copilot-quota-token.json:\n" +
|
|
154
|
+
' {"token": "github_pat_xxx...", "username": "YourUsername"}\n\n' +
|
|
155
|
+
"Alternatives:\n" +
|
|
156
|
+
"• Click the Copilot icon in VS Code status bar to view quota\n" +
|
|
157
|
+
"• Visit https://github.com/settings/billing for usage info",
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// 导出
|
|
162
|
+
// ============================================================================
|
|
163
|
+
/** 当前语言(模块加载时检测一次) */
|
|
164
|
+
export const currentLang = detectLanguage();
|
|
165
|
+
/** 翻译函数 */
|
|
166
|
+
export const t = translations[currentLang];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI 额度查询模块
|
|
3
|
+
*
|
|
4
|
+
* [输入]: OAuth access token
|
|
5
|
+
* [输出]: 格式化的额度使用情况
|
|
6
|
+
* [定位]: 被 mystatus.ts 调用,处理 OpenAI 账号
|
|
7
|
+
* [同步]: mystatus.ts, types.ts, utils.ts, i18n.ts
|
|
8
|
+
*/
|
|
9
|
+
import type { OpenAIAuthData, QueryResult } from "./types";
|
|
10
|
+
export type { OpenAIAuthData };
|
|
11
|
+
/**
|
|
12
|
+
* 查询 OpenAI 账号额度
|
|
13
|
+
* @param authData OpenAI 认证数据
|
|
14
|
+
* @returns 查询结果,如果账号不存在或无效返回 null
|
|
15
|
+
*/
|
|
16
|
+
export declare function queryOpenAIUsage(authData: OpenAIAuthData | undefined): Promise<QueryResult | null>;
|
|
17
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../plugin/lib/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AA6L3D,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,cAAc,GAAG,SAAS,GACnC,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA2B7B"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI 额度查询模块
|
|
3
|
+
*
|
|
4
|
+
* [输入]: OAuth access token
|
|
5
|
+
* [输出]: 格式化的额度使用情况
|
|
6
|
+
* [定位]: 被 mystatus.ts 调用,处理 OpenAI 账号
|
|
7
|
+
* [同步]: mystatus.ts, types.ts, utils.ts, i18n.ts
|
|
8
|
+
*/
|
|
9
|
+
import { t } from "./i18n";
|
|
10
|
+
import { calcRemainPercent, createProgressBar, fetchWithTimeout, formatDuration, } from "./utils";
|
|
11
|
+
/**
|
|
12
|
+
* Base64URL 解码
|
|
13
|
+
*/
|
|
14
|
+
function base64UrlDecode(input) {
|
|
15
|
+
const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
16
|
+
const padLen = (4 - (base64.length % 4)) % 4;
|
|
17
|
+
const padded = base64 + "=".repeat(padLen);
|
|
18
|
+
return Buffer.from(padded, "base64").toString("utf8");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 解析 JWT token,提取 payload
|
|
22
|
+
*/
|
|
23
|
+
function parseJwt(token) {
|
|
24
|
+
try {
|
|
25
|
+
const parts = token.split(".");
|
|
26
|
+
if (parts.length !== 3)
|
|
27
|
+
return null;
|
|
28
|
+
const payloadJson = base64UrlDecode(parts[1]);
|
|
29
|
+
return JSON.parse(payloadJson);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 从 JWT 中提取用户邮箱
|
|
37
|
+
*/
|
|
38
|
+
function getEmailFromJwt(token) {
|
|
39
|
+
const payload = parseJwt(token);
|
|
40
|
+
return payload?.["https://api.openai.com/profile"]?.email ?? null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 从 JWT 中提取 ChatGPT 账号 ID(用于团队/组织账号)
|
|
44
|
+
*/
|
|
45
|
+
function getAccountIdFromJwt(token) {
|
|
46
|
+
const payload = parseJwt(token);
|
|
47
|
+
return payload?.["https://api.openai.com/auth"]?.chatgpt_account_id ?? null;
|
|
48
|
+
}
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// 工具函数
|
|
51
|
+
// ============================================================================
|
|
52
|
+
/**
|
|
53
|
+
* 将窗口时长(秒)转换为可读的窗口名称
|
|
54
|
+
*/
|
|
55
|
+
function formatWindowName(seconds) {
|
|
56
|
+
const days = Math.round(seconds / 86400);
|
|
57
|
+
if (days >= 1) {
|
|
58
|
+
return t.dayLimit(days);
|
|
59
|
+
}
|
|
60
|
+
return t.hourLimit(Math.round(seconds / 3600));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 格式化单个窗口的使用情况
|
|
64
|
+
*/
|
|
65
|
+
function formatWindow(window) {
|
|
66
|
+
const windowName = formatWindowName(window.limit_window_seconds);
|
|
67
|
+
const remainPercent = calcRemainPercent(window.used_percent);
|
|
68
|
+
const progressBar = createProgressBar(remainPercent);
|
|
69
|
+
const resetTime = formatDuration(window.reset_after_seconds);
|
|
70
|
+
return [
|
|
71
|
+
windowName,
|
|
72
|
+
`${progressBar} ${t.remaining(remainPercent)}`,
|
|
73
|
+
t.resetIn(resetTime),
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// API 调用
|
|
78
|
+
// ============================================================================
|
|
79
|
+
const OPENAI_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
80
|
+
/**
|
|
81
|
+
* 获取 OpenAI 使用情况
|
|
82
|
+
*/
|
|
83
|
+
async function fetchOpenAIUsage(accessToken) {
|
|
84
|
+
const headers = {
|
|
85
|
+
Authorization: `Bearer ${accessToken}`,
|
|
86
|
+
"User-Agent": "OpenCode-Status-Plugin/1.0",
|
|
87
|
+
};
|
|
88
|
+
const accountId = getAccountIdFromJwt(accessToken);
|
|
89
|
+
if (accountId) {
|
|
90
|
+
headers["ChatGPT-Account-Id"] = accountId;
|
|
91
|
+
}
|
|
92
|
+
const response = await fetchWithTimeout(OPENAI_USAGE_URL, {
|
|
93
|
+
headers,
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const errorText = await response.text();
|
|
97
|
+
throw new Error(t.apiError(response.status, errorText));
|
|
98
|
+
}
|
|
99
|
+
return response.json();
|
|
100
|
+
}
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// 格式化输出
|
|
103
|
+
// ============================================================================
|
|
104
|
+
/**
|
|
105
|
+
* 格式化 OpenAI 使用情况
|
|
106
|
+
*/
|
|
107
|
+
function formatOpenAIUsage(data, email) {
|
|
108
|
+
const { plan_type, rate_limit } = data;
|
|
109
|
+
const lines = [];
|
|
110
|
+
// 标题行:Account: email (plan)
|
|
111
|
+
const accountDisplay = email || t.unknown;
|
|
112
|
+
lines.push(`${t.account} ${accountDisplay} (${plan_type})`);
|
|
113
|
+
lines.push("");
|
|
114
|
+
// 主窗口
|
|
115
|
+
if (rate_limit?.primary_window) {
|
|
116
|
+
lines.push(...formatWindow(rate_limit.primary_window));
|
|
117
|
+
}
|
|
118
|
+
// 次窗口(如果存在)
|
|
119
|
+
if (rate_limit?.secondary_window) {
|
|
120
|
+
lines.push("");
|
|
121
|
+
lines.push(...formatWindow(rate_limit.secondary_window));
|
|
122
|
+
}
|
|
123
|
+
// 限额状态提示
|
|
124
|
+
if (rate_limit?.limit_reached) {
|
|
125
|
+
lines.push("");
|
|
126
|
+
lines.push(t.limitReached);
|
|
127
|
+
}
|
|
128
|
+
return lines.join("\n");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 查询 OpenAI 账号额度
|
|
132
|
+
* @param authData OpenAI 认证数据
|
|
133
|
+
* @returns 查询结果,如果账号不存在或无效返回 null
|
|
134
|
+
*/
|
|
135
|
+
export async function queryOpenAIUsage(authData) {
|
|
136
|
+
// 检查账号是否存在且有效
|
|
137
|
+
if (authData?.type !== "oauth" || !authData.access) {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
// 检查 OAuth token 是否过期
|
|
141
|
+
if (authData.expires && authData.expires < Date.now()) {
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
error: t.tokenExpired,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const email = getEmailFromJwt(authData.access);
|
|
149
|
+
const usage = await fetchOpenAIUsage(authData.access);
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
output: formatOpenAIUsage(usage, email),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return {
|
|
157
|
+
success: false,
|
|
158
|
+
error: err instanceof Error ? err.message : String(err),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享类型定义
|
|
3
|
+
*
|
|
4
|
+
* [定位]: 被所有平台模块共享使用的类型
|
|
5
|
+
* [同步]: openai.ts, zhipu.ts, google.ts, mystatus.ts
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 平台查询结果
|
|
9
|
+
*/
|
|
10
|
+
export interface QueryResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
output?: string;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* OpenAI OAuth 认证数据
|
|
17
|
+
*/
|
|
18
|
+
export interface OpenAIAuthData {
|
|
19
|
+
type: string;
|
|
20
|
+
access?: string;
|
|
21
|
+
refresh?: string;
|
|
22
|
+
expires?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 智谱 AI API 认证数据
|
|
26
|
+
*/
|
|
27
|
+
export interface ZhipuAuthData {
|
|
28
|
+
type: string;
|
|
29
|
+
key?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* GitHub Copilot 认证数据
|
|
33
|
+
*/
|
|
34
|
+
export interface CopilotAuthData {
|
|
35
|
+
type: string;
|
|
36
|
+
refresh?: string;
|
|
37
|
+
access?: string;
|
|
38
|
+
expires?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Copilot subscription tier
|
|
42
|
+
* See: https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot
|
|
43
|
+
*/
|
|
44
|
+
export type CopilotTier = "free" | "pro" | "pro+" | "business" | "enterprise";
|
|
45
|
+
/**
|
|
46
|
+
* Copilot quota token configuration
|
|
47
|
+
* Stored in ~/.config/opencode/copilot-quota-token.json
|
|
48
|
+
*
|
|
49
|
+
* Users can create a fine-grained PAT with "Plan" read permission
|
|
50
|
+
* to enable quota checking via the public GitHub REST API.
|
|
51
|
+
*/
|
|
52
|
+
export interface CopilotQuotaConfig {
|
|
53
|
+
/** Fine-grained PAT with "Plan" read permission */
|
|
54
|
+
token: string;
|
|
55
|
+
/** GitHub username (for API calls) */
|
|
56
|
+
username: string;
|
|
57
|
+
/** Copilot subscription tier (determines monthly quota limit) */
|
|
58
|
+
tier: CopilotTier;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Antigravity 账号(来自 ~/.config/opencode/antigravity-accounts.json)
|
|
62
|
+
*/
|
|
63
|
+
export interface AntigravityAccount {
|
|
64
|
+
email?: string;
|
|
65
|
+
refreshToken: string;
|
|
66
|
+
projectId?: string;
|
|
67
|
+
managedProjectId?: string;
|
|
68
|
+
addedAt: number;
|
|
69
|
+
lastUsed: number;
|
|
70
|
+
rateLimitResetTimes?: Record<string, number>;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Antigravity 账号文件
|
|
74
|
+
*/
|
|
75
|
+
export interface AntigravityAccountsFile {
|
|
76
|
+
version: number;
|
|
77
|
+
accounts: AntigravityAccount[];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 完整认证数据结构
|
|
81
|
+
*/
|
|
82
|
+
export interface AuthData {
|
|
83
|
+
openai?: OpenAIAuthData;
|
|
84
|
+
"zhipuai-coding-plan"?: ZhipuAuthData;
|
|
85
|
+
"zai-coding-plan"?: ZhipuAuthData;
|
|
86
|
+
"github-copilot"?: CopilotAuthData;
|
|
87
|
+
}
|
|
88
|
+
/** 高使用率警告阈值(百分比) */
|
|
89
|
+
export declare const HIGH_USAGE_THRESHOLD = 80;
|
|
90
|
+
/** API 请求超时时间(毫秒) */
|
|
91
|
+
export declare const REQUEST_TIMEOUT_MS = 10000;
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../plugin/lib/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,GAAG,YAAY,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,qBAAqB,CAAC,EAAE,aAAa,CAAC;IACtC,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,gBAAgB,CAAC,EAAE,eAAe,CAAC;CACpC;AAMD,oBAAoB;AACpB,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,qBAAqB;AACrB,eAAO,MAAM,kBAAkB,QAAQ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享类型定义
|
|
3
|
+
*
|
|
4
|
+
* [定位]: 被所有平台模块共享使用的类型
|
|
5
|
+
* [同步]: openai.ts, zhipu.ts, google.ts, mystatus.ts
|
|
6
|
+
*/
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// 常量配置
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/** 高使用率警告阈值(百分比) */
|
|
11
|
+
export const HIGH_USAGE_THRESHOLD = 80;
|
|
12
|
+
/** API 请求超时时间(毫秒) */
|
|
13
|
+
export const REQUEST_TIMEOUT_MS = 10000;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 共享工具函数
|
|
3
|
+
*
|
|
4
|
+
* [定位]: 被所有平台模块共享使用的工具函数
|
|
5
|
+
* [同步]: openai.ts, zhipu.ts, google.ts
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* 将秒数转换为人类可读的时间格式
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatDuration(seconds: number): string;
|
|
11
|
+
/**
|
|
12
|
+
* 生成进度条(实心代表剩余额度)
|
|
13
|
+
* @param remainPercent 剩余百分比 (0-100)
|
|
14
|
+
* @param width 进度条宽度(字符数)
|
|
15
|
+
*/
|
|
16
|
+
export declare function createProgressBar(remainPercent: number, width?: number): string;
|
|
17
|
+
/**
|
|
18
|
+
* 计算剩余百分比并取整
|
|
19
|
+
* @param usedPercent 已使用百分比
|
|
20
|
+
*/
|
|
21
|
+
export declare function calcRemainPercent(usedPercent: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* 格式化 Token 数量(以百万为单位)
|
|
24
|
+
*/
|
|
25
|
+
export declare function formatTokens(tokens: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* 带超时的 fetch 请求
|
|
28
|
+
* @param url 请求 URL
|
|
29
|
+
* @param options fetch 选项
|
|
30
|
+
* @param timeoutMs 超时时间(毫秒),默认使用全局配置
|
|
31
|
+
*/
|
|
32
|
+
export declare function fetchWithTimeout(url: string, options: RequestInit, timeoutMs?: number): Promise<Response>;
|
|
33
|
+
/**
|
|
34
|
+
* 安全获取数组最大值,空数组返回 0
|
|
35
|
+
*/
|
|
36
|
+
export declare function safeMax(arr: number[]): number;
|
|
37
|
+
/**
|
|
38
|
+
* 脱敏显示敏感字符串
|
|
39
|
+
* 显示前 N 位和后 N 位,中间用 **** 替代
|
|
40
|
+
* @param str 原始字符串
|
|
41
|
+
* @param showChars 前后各显示的字符数,默认 4
|
|
42
|
+
*/
|
|
43
|
+
export declare function maskString(str: string, showChars?: number): string;
|
|
44
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../plugin/lib/utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAWtD;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,aAAa,EAAE,MAAM,EACrB,KAAK,GAAE,MAAW,GACjB,MAAM,CAUR;AAMD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAMD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,WAAW,EACpB,SAAS,GAAE,MAA2B,GACrC,OAAO,CAAC,QAAQ,CAAC,CAkBnB;AAMD;;GAEG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,CAG7C;AAMD;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAU,GAAG,MAAM,CAKrE"}
|