@ekworp/opencode-mystatus 0.0.0-stage → 1.2.8

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.
@@ -0,0 +1,111 @@
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- DeepSeek\n- Google Cloud (Antigravity)";
30
+ readonly queryFailed: "❌ 查询失败的账号:\n";
31
+ readonly openaiTitle: "## OpenAI 账号额度";
32
+ readonly zhipuTitle: "## 智谱 AI 账号额度";
33
+ readonly zaiTitle: "## Z.ai 账号额度";
34
+ readonly deepseekTitle: "## DeepSeek 账户余额";
35
+ readonly zhipuApiError: (status: number, text: string) => string;
36
+ readonly zaiApiError: (status: number, text: string) => string;
37
+ readonly zhipuTokensLimit: "5 小时 Token 限额";
38
+ readonly zhipuMcpLimit: "MCP 月度配额";
39
+ readonly zhipuAccountName: "Coding Plan";
40
+ readonly zaiAccountName: "Z.ai";
41
+ readonly deepseekApiError: (status: number, text: string) => string;
42
+ readonly deepseekAccountName: "API 账户";
43
+ readonly deepseekBalance: "账户余额";
44
+ readonly deepseekNoBalance: "暂无余额数据";
45
+ readonly noQuotaData: "暂无配额数据";
46
+ readonly googleTitle: "## Google Cloud 账号额度";
47
+ readonly googleApiError: (status: number, text: string) => string;
48
+ readonly googleNoProjectId: "⚠️ 缺少 project_id,无法查询额度。";
49
+ readonly copilotTitle: "## GitHub Copilot 账号额度";
50
+ readonly copilotApiError: (status: number, text: string) => string;
51
+ readonly premiumRequests: "Premium";
52
+ readonly chatQuota: "Chat";
53
+ readonly completionsQuota: "Completions";
54
+ readonly overage: "超额使用";
55
+ readonly overageRequests: "次请求";
56
+ readonly quotaResets: "配额重置";
57
+ readonly resetsSoon: "即将重置";
58
+ readonly modelBreakdown: "模型使用明细:";
59
+ readonly billingPeriod: "计费周期";
60
+ readonly copilotQuotaUnavailable: string;
61
+ readonly copilotQuotaWorkaround: string;
62
+ } | {
63
+ readonly days: (n: number) => string;
64
+ readonly hours: (n: number) => string;
65
+ readonly minutes: (n: number) => string;
66
+ readonly hourLimit: (h: number) => string;
67
+ readonly dayLimit: (d: number) => string;
68
+ readonly remaining: (p: number) => string;
69
+ readonly resetIn: (t: string) => string;
70
+ readonly limitReached: "⚠️ Rate limit reached!";
71
+ readonly account: "Account:";
72
+ readonly unknown: "unknown";
73
+ readonly used: "Used";
74
+ readonly authError: (path: string, err: string) => string;
75
+ readonly apiError: (status: number, text: string) => string;
76
+ readonly timeoutError: (seconds: number) => string;
77
+ readonly tokenExpired: "⚠️ OAuth token expired. Please use an OpenAI model in OpenCode to refresh authorization.";
78
+ 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- DeepSeek\n- Google Cloud (Antigravity)";
79
+ readonly queryFailed: "❌ Failed to query accounts:\n";
80
+ readonly openaiTitle: "## OpenAI Account Quota";
81
+ readonly zhipuTitle: "## Zhipu AI Account Quota";
82
+ readonly zaiTitle: "## Z.ai Account Quota";
83
+ readonly deepseekTitle: "## DeepSeek Account Balance";
84
+ readonly zhipuApiError: (status: number, text: string) => string;
85
+ readonly zaiApiError: (status: number, text: string) => string;
86
+ readonly zhipuTokensLimit: "5-hour token limit";
87
+ readonly zhipuMcpLimit: "MCP monthly quota";
88
+ readonly zhipuAccountName: "Coding Plan";
89
+ readonly zaiAccountName: "Z.ai";
90
+ readonly deepseekApiError: (status: number, text: string) => string;
91
+ readonly deepseekAccountName: "API account";
92
+ readonly deepseekBalance: "Account balance";
93
+ readonly deepseekNoBalance: "No balance data available";
94
+ readonly noQuotaData: "No quota data available";
95
+ readonly googleTitle: "## Google Cloud Account Quota";
96
+ readonly googleApiError: (status: number, text: string) => string;
97
+ readonly googleNoProjectId: "⚠️ Missing project_id, cannot query quota.";
98
+ readonly copilotTitle: "## GitHub Copilot Account Quota";
99
+ readonly copilotApiError: (status: number, text: string) => string;
100
+ readonly premiumRequests: "Premium";
101
+ readonly chatQuota: "Chat";
102
+ readonly completionsQuota: "Completions";
103
+ readonly overage: "Overage";
104
+ readonly overageRequests: "requests";
105
+ readonly quotaResets: "Quota resets";
106
+ readonly resetsSoon: "Resets soon";
107
+ readonly modelBreakdown: "Model breakdown:";
108
+ readonly billingPeriod: "Period";
109
+ readonly copilotQuotaUnavailable: string;
110
+ readonly copilotQuotaWorkaround: string;
111
+ };
@@ -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,176 @@
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) => `❌ 无法读取 OpenCode 凭证数据库: ${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- DeepSeek\n- Google Cloud (Antigravity)",
58
+ queryFailed: "❌ 查询失败的账号:\n",
59
+ // 平台标题
60
+ openaiTitle: "## OpenAI 账号额度",
61
+ zhipuTitle: "## 智谱 AI 账号额度",
62
+ zaiTitle: "## Z.ai 账号额度",
63
+ deepseekTitle: "## DeepSeek 账户余额",
64
+ // 智谱 AI 相关
65
+ zhipuApiError: (status, text) => `智谱 API 请求失败 (${status}): ${text}`,
66
+ zaiApiError: (status, text) => `Z.ai API 请求失败 (${status}): ${text}`,
67
+ zhipuTokensLimit: "5 小时 Token 限额",
68
+ zhipuMcpLimit: "MCP 月度配额",
69
+ zhipuAccountName: "Coding Plan",
70
+ zaiAccountName: "Z.ai",
71
+ deepseekApiError: (status, text) => `DeepSeek API 请求失败 (${status}): ${text}`,
72
+ deepseekAccountName: "API 账户",
73
+ deepseekBalance: "账户余额",
74
+ deepseekNoBalance: "暂无余额数据",
75
+ noQuotaData: "暂无配额数据",
76
+ // Google 相关
77
+ googleTitle: "## Google Cloud 账号额度",
78
+ googleApiError: (status, text) => `Google API 请求失败 (${status}): ${text}`,
79
+ googleNoProjectId: "⚠️ 缺少 project_id,无法查询额度。",
80
+ // GitHub Copilot 相关
81
+ copilotTitle: "## GitHub Copilot 账号额度",
82
+ copilotApiError: (status, text) => `GitHub Copilot API 请求失败 (${status}): ${text}`,
83
+ premiumRequests: "Premium",
84
+ chatQuota: "Chat",
85
+ completionsQuota: "Completions",
86
+ overage: "超额使用",
87
+ overageRequests: "次请求",
88
+ quotaResets: "配额重置",
89
+ resetsSoon: "即将重置",
90
+ modelBreakdown: "模型使用明细:",
91
+ billingPeriod: "计费周期",
92
+ copilotQuotaUnavailable: "⚠️ GitHub Copilot 配额查询暂时不可用。\n" +
93
+ "OpenCode 的新 OAuth 集成不支持访问配额 API。",
94
+ copilotQuotaWorkaround: "解决方案:\n" +
95
+ "1. 创建一个 fine-grained PAT (访问 https://github.com/settings/tokens?type=beta)\n" +
96
+ "2. 在 'Account permissions' 中将 'Plan' 设为 'Read-only'\n" +
97
+ "3. 创建配置文件 ~/.config/opencode/copilot-quota-token.json:\n" +
98
+ ' {"token": "github_pat_xxx...", "username": "你的用户名"}\n\n' +
99
+ "其他方法:\n" +
100
+ "• 在 VS Code 中点击状态栏的 Copilot 图标查看配额\n" +
101
+ "• 访问 https://github.com/settings/billing 查看使用情况",
102
+ },
103
+ en: {
104
+ // 时间单位
105
+ days: (n) => `${n}d`,
106
+ hours: (n) => `${n}h`,
107
+ minutes: (n) => `${n}m`,
108
+ // 限额相关
109
+ hourLimit: (h) => `${h}-hour limit`,
110
+ dayLimit: (d) => `${d}-day limit`,
111
+ remaining: (p) => `${p}% remaining`,
112
+ resetIn: (t) => `Resets in: ${t}`,
113
+ limitReached: "⚠️ Rate limit reached!",
114
+ // 通用
115
+ account: "Account:",
116
+ unknown: "unknown",
117
+ used: "Used",
118
+ // 错误信息
119
+ authError: (path, err) => `❌ Failed to read OpenCode credential database: ${path}\nError: ${err}`,
120
+ apiError: (status, text) => `OpenAI API request failed (${status}): ${text}`,
121
+ timeoutError: (seconds) => `Request timeout (${seconds}s)`,
122
+ tokenExpired: "⚠️ OAuth token expired. Please use an OpenAI model in OpenCode to refresh authorization.",
123
+ 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- DeepSeek\n- Google Cloud (Antigravity)",
124
+ queryFailed: "❌ Failed to query accounts:\n",
125
+ // 平台标题
126
+ openaiTitle: "## OpenAI Account Quota",
127
+ zhipuTitle: "## Zhipu AI Account Quota",
128
+ zaiTitle: "## Z.ai Account Quota",
129
+ deepseekTitle: "## DeepSeek Account Balance",
130
+ // 智谱 AI 相关
131
+ zhipuApiError: (status, text) => `Zhipu API request failed (${status}): ${text}`,
132
+ zaiApiError: (status, text) => `Z.ai API request failed (${status}): ${text}`,
133
+ zhipuTokensLimit: "5-hour token limit",
134
+ zhipuMcpLimit: "MCP monthly quota",
135
+ zhipuAccountName: "Coding Plan",
136
+ zaiAccountName: "Z.ai",
137
+ deepseekApiError: (status, text) => `DeepSeek API request failed (${status}): ${text}`,
138
+ deepseekAccountName: "API account",
139
+ deepseekBalance: "Account balance",
140
+ deepseekNoBalance: "No balance data available",
141
+ noQuotaData: "No quota data available",
142
+ // Google 相关
143
+ googleTitle: "## Google Cloud Account Quota",
144
+ googleApiError: (status, text) => `Google API request failed (${status}): ${text}`,
145
+ googleNoProjectId: "⚠️ Missing project_id, cannot query quota.",
146
+ // GitHub Copilot 相关
147
+ copilotTitle: "## GitHub Copilot Account Quota",
148
+ copilotApiError: (status, text) => `GitHub Copilot API request failed (${status}): ${text}`,
149
+ premiumRequests: "Premium",
150
+ chatQuota: "Chat",
151
+ completionsQuota: "Completions",
152
+ overage: "Overage",
153
+ overageRequests: "requests",
154
+ quotaResets: "Quota resets",
155
+ resetsSoon: "Resets soon",
156
+ modelBreakdown: "Model breakdown:",
157
+ billingPeriod: "Period",
158
+ copilotQuotaUnavailable: "⚠️ GitHub Copilot quota query unavailable.\n" +
159
+ "OpenCode's new OAuth integration doesn't support quota API access.",
160
+ copilotQuotaWorkaround: "Solution:\n" +
161
+ "1. Create a fine-grained PAT (visit https://github.com/settings/tokens?type=beta)\n" +
162
+ "2. Under 'Account permissions', set 'Plan' to 'Read-only'\n" +
163
+ "3. Create config file ~/.config/opencode/copilot-quota-token.json:\n" +
164
+ ' {"token": "github_pat_xxx...", "username": "YourUsername"}\n\n' +
165
+ "Alternatives:\n" +
166
+ "• Click the Copilot icon in VS Code status bar to view quota\n" +
167
+ "• Visit https://github.com/settings/billing for usage info",
168
+ },
169
+ };
170
+ // ============================================================================
171
+ // 导出
172
+ // ============================================================================
173
+ /** 当前语言(模块加载时检测一次) */
174
+ export const currentLang = detectLanguage();
175
+ /** 翻译函数 */
176
+ export const t = translations[currentLang];
@@ -0,0 +1,16 @@
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>;
@@ -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,9 @@
1
+ /**
2
+ * Read credentials from OpenCode V2's SQLite database.
3
+ *
4
+ * The sqlite3 CLI is used instead of adding a native Node SQLite dependency.
5
+ */
6
+ import type { ProviderId } from "./config";
7
+ import type { AuthData } from "./types";
8
+ /** Read active V2 credentials without modifying the database. */
9
+ export declare function readV2AuthData(databasePath?: string, disabledProviders?: Set<ProviderId>): Promise<AuthData>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Read credentials from OpenCode V2's SQLite database.
3
+ *
4
+ * The sqlite3 CLI is used instead of adding a native Node SQLite dependency.
5
+ */
6
+ import { execFile } from "node:child_process";
7
+ import { homedir } from "node:os";
8
+ import { promisify } from "node:util";
9
+ const execFileAsync = promisify(execFile);
10
+ const DATABASE_PATH = `${homedir()}/.local/share/opencode/opencode.db`;
11
+ /** Read active V2 credentials without modifying the database. */
12
+ export async function readV2AuthData(databasePath = DATABASE_PATH, disabledProviders = new Set()) {
13
+ const providerIds = [
14
+ "openai",
15
+ "zhipuai-coding-plan",
16
+ "zai-coding-plan",
17
+ "github-copilot",
18
+ "deepseek",
19
+ ].filter((provider) => !disabledProviders.has(provider));
20
+ if (providerIds.length === 0)
21
+ return {};
22
+ const sql = `SELECT integration_id, value FROM credential WHERE (active IS NULL OR active != 0) AND integration_id IN (${providerIds.map((provider) => `'${provider}'`).join(", ")}) ORDER BY time_updated DESC;`;
23
+ try {
24
+ const { stdout } = await execFileAsync("sqlite3", ["-json", `file:${databasePath}?mode=ro`, sql], { maxBuffer: 1024 * 1024, windowsHide: true });
25
+ const rows = JSON.parse(stdout);
26
+ const authData = {};
27
+ for (const row of rows) {
28
+ if (!row.integration_id || typeof row.value !== "string")
29
+ continue;
30
+ try {
31
+ const value = JSON.parse(row.value);
32
+ if (value &&
33
+ typeof value === "object" &&
34
+ !(row.integration_id in authData)) {
35
+ // Integration IDs are the same provider IDs used by auth.json.
36
+ authData[row.integration_id] = value;
37
+ }
38
+ }
39
+ catch {
40
+ // Ignore malformed rows and continue reading other providers.
41
+ }
42
+ }
43
+ return authData;
44
+ }
45
+ catch {
46
+ return {};
47
+ }
48
+ }
@@ -0,0 +1,97 @@
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
+ /** DeepSeek API key authentication data */
32
+ export interface DeepSeekAuthData {
33
+ type: string;
34
+ key?: string;
35
+ }
36
+ /**
37
+ * GitHub Copilot 认证数据
38
+ */
39
+ export interface CopilotAuthData {
40
+ type: string;
41
+ refresh?: string;
42
+ access?: string;
43
+ expires?: number;
44
+ }
45
+ /**
46
+ * Copilot subscription tier
47
+ * See: https://docs.github.com/en/copilot/about-github-copilot/subscription-plans-for-github-copilot
48
+ */
49
+ export type CopilotTier = "free" | "pro" | "pro+" | "business" | "enterprise";
50
+ /**
51
+ * Copilot quota token configuration
52
+ * Stored in ~/.config/opencode/copilot-quota-token.json
53
+ *
54
+ * Users can create a fine-grained PAT with "Plan" read permission
55
+ * to enable quota checking via the public GitHub REST API.
56
+ */
57
+ export interface CopilotQuotaConfig {
58
+ /** Fine-grained PAT with "Plan" read permission */
59
+ token: string;
60
+ /** GitHub username (for API calls) */
61
+ username: string;
62
+ /** Copilot subscription tier (determines monthly quota limit) */
63
+ tier: CopilotTier;
64
+ }
65
+ /**
66
+ * Antigravity 账号(来自 ~/.config/opencode/antigravity-accounts.json)
67
+ */
68
+ export interface AntigravityAccount {
69
+ email?: string;
70
+ refreshToken: string;
71
+ projectId?: string;
72
+ managedProjectId?: string;
73
+ addedAt: number;
74
+ lastUsed: number;
75
+ rateLimitResetTimes?: Record<string, number>;
76
+ }
77
+ /**
78
+ * Antigravity 账号文件
79
+ */
80
+ export interface AntigravityAccountsFile {
81
+ version: number;
82
+ accounts: AntigravityAccount[];
83
+ }
84
+ /**
85
+ * 完整认证数据结构
86
+ */
87
+ export interface AuthData {
88
+ openai?: OpenAIAuthData;
89
+ deepseek?: DeepSeekAuthData;
90
+ "zhipuai-coding-plan"?: ZhipuAuthData;
91
+ "zai-coding-plan"?: ZhipuAuthData;
92
+ "github-copilot"?: CopilotAuthData;
93
+ }
94
+ /** 高使用率警告阈值(百分比) */
95
+ export declare const HIGH_USAGE_THRESHOLD = 80;
96
+ /** API 请求超时时间(毫秒) */
97
+ export declare const REQUEST_TIMEOUT_MS = 10000;
@@ -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;