@slkiser/opencode-quota 1.0.0
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 +103 -0
- package/dist/data/modelsdev-pricing.min.json +504 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cache.d.ts +49 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +127 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/config.d.ts +30 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +154 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/copilot.d.ts +25 -0
- package/dist/lib/copilot.d.ts.map +1 -0
- package/dist/lib/copilot.js +306 -0
- package/dist/lib/copilot.js.map +1 -0
- package/dist/lib/entries.d.ts +60 -0
- package/dist/lib/entries.d.ts.map +1 -0
- package/dist/lib/entries.js +8 -0
- package/dist/lib/entries.js.map +1 -0
- package/dist/lib/firmware.d.ts +15 -0
- package/dist/lib/firmware.d.ts.map +1 -0
- package/dist/lib/firmware.js +79 -0
- package/dist/lib/firmware.js.map +1 -0
- package/dist/lib/format.d.ts +16 -0
- package/dist/lib/format.d.ts.map +1 -0
- package/dist/lib/format.js +99 -0
- package/dist/lib/format.js.map +1 -0
- package/dist/lib/google-token-cache.d.ts +30 -0
- package/dist/lib/google-token-cache.d.ts.map +1 -0
- package/dist/lib/google-token-cache.js +100 -0
- package/dist/lib/google-token-cache.js.map +1 -0
- package/dist/lib/google.d.ts +43 -0
- package/dist/lib/google.d.ts.map +1 -0
- package/dist/lib/google.js +399 -0
- package/dist/lib/google.js.map +1 -0
- package/dist/lib/markdown-table.d.ts +8 -0
- package/dist/lib/markdown-table.d.ts.map +1 -0
- package/dist/lib/markdown-table.js +56 -0
- package/dist/lib/markdown-table.js.map +1 -0
- package/dist/lib/modelsdev-pricing.d.ts +23 -0
- package/dist/lib/modelsdev-pricing.d.ts.map +1 -0
- package/dist/lib/modelsdev-pricing.js +32 -0
- package/dist/lib/modelsdev-pricing.js.map +1 -0
- package/dist/lib/openai.d.ts +33 -0
- package/dist/lib/openai.d.ts.map +1 -0
- package/dist/lib/openai.js +162 -0
- package/dist/lib/openai.js.map +1 -0
- package/dist/lib/opencode-auth.d.ts +11 -0
- package/dist/lib/opencode-auth.d.ts.map +1 -0
- package/dist/lib/opencode-auth.js +27 -0
- package/dist/lib/opencode-auth.js.map +1 -0
- package/dist/lib/opencode-storage.d.ts +45 -0
- package/dist/lib/opencode-storage.d.ts.map +1 -0
- package/dist/lib/opencode-storage.js +120 -0
- package/dist/lib/opencode-storage.js.map +1 -0
- package/dist/lib/quota-command-format.d.ts +14 -0
- package/dist/lib/quota-command-format.d.ts.map +1 -0
- package/dist/lib/quota-command-format.js +122 -0
- package/dist/lib/quota-command-format.js.map +1 -0
- package/dist/lib/quota-stats-format.d.ts +9 -0
- package/dist/lib/quota-stats-format.d.ts.map +1 -0
- package/dist/lib/quota-stats-format.js +244 -0
- package/dist/lib/quota-stats-format.js.map +1 -0
- package/dist/lib/quota-stats.d.ts +71 -0
- package/dist/lib/quota-stats.d.ts.map +1 -0
- package/dist/lib/quota-stats.js +270 -0
- package/dist/lib/quota-stats.js.map +1 -0
- package/dist/lib/quota-status.d.ts +23 -0
- package/dist/lib/quota-status.d.ts.map +1 -0
- package/dist/lib/quota-status.js +112 -0
- package/dist/lib/quota-status.js.map +1 -0
- package/dist/lib/toast-format-grouped.d.ts +25 -0
- package/dist/lib/toast-format-grouped.d.ts.map +1 -0
- package/dist/lib/toast-format-grouped.js +123 -0
- package/dist/lib/toast-format-grouped.js.map +1 -0
- package/dist/lib/types.d.ts +218 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +39 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/plugin.d.ts +13 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +633 -0
- package/dist/plugin.js.map +1 -0
- package/dist/providers/copilot.d.ts +8 -0
- package/dist/providers/copilot.d.ts.map +1 -0
- package/dist/providers/copilot.js +50 -0
- package/dist/providers/copilot.js.map +1 -0
- package/dist/providers/firmware.d.ts +6 -0
- package/dist/providers/firmware.d.ts.map +1 -0
- package/dist/providers/firmware.js +52 -0
- package/dist/providers/firmware.js.map +1 -0
- package/dist/providers/google-antigravity.d.ts +6 -0
- package/dist/providers/google-antigravity.d.ts.map +1 -0
- package/dist/providers/google-antigravity.js +77 -0
- package/dist/providers/google-antigravity.js.map +1 -0
- package/dist/providers/openai.d.ts +6 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +106 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.d.ts +8 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +14 -0
- package/dist/providers/registry.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache and throttling system for quota fetching
|
|
3
|
+
*
|
|
4
|
+
* Implements:
|
|
5
|
+
* - Throttling: Only fetch if minIntervalMs has passed since last fetch
|
|
6
|
+
* - Caching: Store last toast message for immediate display
|
|
7
|
+
* - Deduplication: Use inFlightPromise to prevent concurrent fetches
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Get the cached toast message if still valid
|
|
11
|
+
*
|
|
12
|
+
* @param minIntervalMs - Minimum interval between fetches
|
|
13
|
+
* @returns Cached message or null if stale/missing
|
|
14
|
+
*/
|
|
15
|
+
export declare function getCachedToast(minIntervalMs: number): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* Check if a new fetch should be initiated
|
|
18
|
+
*
|
|
19
|
+
* @param minIntervalMs - Minimum interval between fetches
|
|
20
|
+
* @returns true if a fetch should be started
|
|
21
|
+
*/
|
|
22
|
+
export declare function shouldFetch(minIntervalMs: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Get or start a fetch operation with deduplication
|
|
25
|
+
*
|
|
26
|
+
* @param fetchFn - Function that performs the actual fetch
|
|
27
|
+
* @param minIntervalMs - Minimum interval between fetches
|
|
28
|
+
* @returns Promise resolving to toast message or null
|
|
29
|
+
*/
|
|
30
|
+
export declare function getOrFetch(fetchFn: () => Promise<string | null>, minIntervalMs: number): Promise<string | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Get or start a fetch operation with deduplication and cache control.
|
|
33
|
+
*
|
|
34
|
+
* This is useful when some results should be displayed but not cached
|
|
35
|
+
* (e.g. transient "all providers failed" cases).
|
|
36
|
+
*/
|
|
37
|
+
export declare function getOrFetchWithCacheControl(fetchFn: () => Promise<{
|
|
38
|
+
message: string | null;
|
|
39
|
+
cache?: boolean;
|
|
40
|
+
}>, minIntervalMs: number): Promise<string | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Clear the cache (for testing or reset)
|
|
43
|
+
*/
|
|
44
|
+
export declare function clearCache(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Force update the cache with a new message
|
|
47
|
+
*/
|
|
48
|
+
export declare function updateCache(message: string): void;
|
|
49
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcnE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,EACrC,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxB;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EACnE,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqDxB;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAIjC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKjD"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache and throttling system for quota fetching
|
|
3
|
+
*
|
|
4
|
+
* Implements:
|
|
5
|
+
* - Throttling: Only fetch if minIntervalMs has passed since last fetch
|
|
6
|
+
* - Caching: Store last toast message for immediate display
|
|
7
|
+
* - Deduplication: Use inFlightPromise to prevent concurrent fetches
|
|
8
|
+
*/
|
|
9
|
+
/** Cached toast data */
|
|
10
|
+
let cachedToast = null;
|
|
11
|
+
/** In-flight promise for deduplication */
|
|
12
|
+
let inFlightPromise = null;
|
|
13
|
+
/** Timestamp of last fetch start */
|
|
14
|
+
let lastFetchTime = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Get the cached toast message if still valid
|
|
17
|
+
*
|
|
18
|
+
* @param minIntervalMs - Minimum interval between fetches
|
|
19
|
+
* @returns Cached message or null if stale/missing
|
|
20
|
+
*/
|
|
21
|
+
export function getCachedToast(minIntervalMs) {
|
|
22
|
+
if (!cachedToast) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const age = now - cachedToast.timestamp;
|
|
27
|
+
// Return cached value if within throttle window
|
|
28
|
+
if (age < minIntervalMs) {
|
|
29
|
+
return cachedToast.message;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if a new fetch should be initiated
|
|
35
|
+
*
|
|
36
|
+
* @param minIntervalMs - Minimum interval between fetches
|
|
37
|
+
* @returns true if a fetch should be started
|
|
38
|
+
*/
|
|
39
|
+
export function shouldFetch(minIntervalMs) {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
return now - lastFetchTime >= minIntervalMs;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get or start a fetch operation with deduplication
|
|
45
|
+
*
|
|
46
|
+
* @param fetchFn - Function that performs the actual fetch
|
|
47
|
+
* @param minIntervalMs - Minimum interval between fetches
|
|
48
|
+
* @returns Promise resolving to toast message or null
|
|
49
|
+
*/
|
|
50
|
+
export async function getOrFetch(fetchFn, minIntervalMs) {
|
|
51
|
+
const wrapped = async () => {
|
|
52
|
+
const message = await fetchFn();
|
|
53
|
+
return { message, cache: true };
|
|
54
|
+
};
|
|
55
|
+
return getOrFetchWithCacheControl(wrapped, minIntervalMs);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get or start a fetch operation with deduplication and cache control.
|
|
59
|
+
*
|
|
60
|
+
* This is useful when some results should be displayed but not cached
|
|
61
|
+
* (e.g. transient "all providers failed" cases).
|
|
62
|
+
*/
|
|
63
|
+
export async function getOrFetchWithCacheControl(fetchFn, minIntervalMs) {
|
|
64
|
+
// Check cache first
|
|
65
|
+
const cached = getCachedToast(minIntervalMs);
|
|
66
|
+
if (cached !== null) {
|
|
67
|
+
return cached;
|
|
68
|
+
}
|
|
69
|
+
// If there's already a fetch in progress, wait for it
|
|
70
|
+
if (inFlightPromise) {
|
|
71
|
+
return inFlightPromise;
|
|
72
|
+
}
|
|
73
|
+
// Check if we should start a new fetch
|
|
74
|
+
if (!shouldFetch(minIntervalMs)) {
|
|
75
|
+
// Within throttle window but cache is empty/stale
|
|
76
|
+
// Return cached message anyway if available
|
|
77
|
+
return cachedToast?.message ?? null;
|
|
78
|
+
}
|
|
79
|
+
// Start a new fetch
|
|
80
|
+
lastFetchTime = Date.now();
|
|
81
|
+
inFlightPromise = (async () => {
|
|
82
|
+
try {
|
|
83
|
+
const out = await fetchFn();
|
|
84
|
+
const result = out.message;
|
|
85
|
+
const cache = out.cache ?? true;
|
|
86
|
+
// If there is no message, don't throttle future fetches.
|
|
87
|
+
// This is important when users enable providers or connect accounts and
|
|
88
|
+
// want the toast to appear on the next trigger.
|
|
89
|
+
if (result === null) {
|
|
90
|
+
lastFetchTime = 0;
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (!cache) {
|
|
94
|
+
// Display, but don't cache or throttle the next attempt.
|
|
95
|
+
lastFetchTime = 0;
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
cachedToast = {
|
|
99
|
+
message: result,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
};
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
inFlightPromise = null;
|
|
106
|
+
}
|
|
107
|
+
})();
|
|
108
|
+
return inFlightPromise;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Clear the cache (for testing or reset)
|
|
112
|
+
*/
|
|
113
|
+
export function clearCache() {
|
|
114
|
+
cachedToast = null;
|
|
115
|
+
inFlightPromise = null;
|
|
116
|
+
lastFetchTime = 0;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Force update the cache with a new message
|
|
120
|
+
*/
|
|
121
|
+
export function updateCache(message) {
|
|
122
|
+
cachedToast = {
|
|
123
|
+
message,
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader for opencode-quota plugin
|
|
3
|
+
*
|
|
4
|
+
* Primary: reads configuration from OpenCode's merged config via the SDK client.
|
|
5
|
+
* Fallback: reads local config files directly.
|
|
6
|
+
*/
|
|
7
|
+
import type { QuotaToastConfig } from "./types.js";
|
|
8
|
+
export interface LoadConfigMeta {
|
|
9
|
+
source: "sdk" | "files" | "defaults";
|
|
10
|
+
paths: string[];
|
|
11
|
+
}
|
|
12
|
+
export declare function createLoadConfigMeta(): LoadConfigMeta;
|
|
13
|
+
/**
|
|
14
|
+
* Load plugin configuration from OpenCode config
|
|
15
|
+
*
|
|
16
|
+
* @param client - OpenCode SDK client
|
|
17
|
+
* @returns Merged configuration with defaults
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadConfig(client: {
|
|
20
|
+
config: {
|
|
21
|
+
get: () => Promise<{
|
|
22
|
+
data?: {
|
|
23
|
+
experimental?: {
|
|
24
|
+
quotaToast?: Partial<QuotaToastConfig>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
}, meta?: LoadConfigMeta): Promise<QuotaToastConfig>;
|
|
30
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAiB,MAAM,YAAY,CAAC;AAQlE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,CAAC;IACrC,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AASD;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IACN,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,YAAY,CAAC,EAAE;oBAAE,UAAU,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAC9F,CAAC;CACH,EACD,IAAI,CAAC,EAAE,cAAc,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAiK3B"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loader for opencode-quota plugin
|
|
3
|
+
*
|
|
4
|
+
* Primary: reads configuration from OpenCode's merged config via the SDK client.
|
|
5
|
+
* Fallback: reads local config files directly.
|
|
6
|
+
*/
|
|
7
|
+
import { DEFAULT_CONFIG } from "./types.js";
|
|
8
|
+
import { existsSync } from "fs";
|
|
9
|
+
import { readFile } from "fs/promises";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
export function createLoadConfigMeta() {
|
|
13
|
+
return { source: "defaults", paths: [] };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validates and normalizes a Google model ID
|
|
17
|
+
*/
|
|
18
|
+
function isValidGoogleModelId(id) {
|
|
19
|
+
return typeof id === "string" && ["G3PRO", "G3FLASH", "CLAUDE", "G3IMAGE"].includes(id);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Load plugin configuration from OpenCode config
|
|
23
|
+
*
|
|
24
|
+
* @param client - OpenCode SDK client
|
|
25
|
+
* @returns Merged configuration with defaults
|
|
26
|
+
*/
|
|
27
|
+
export async function loadConfig(client, meta) {
|
|
28
|
+
function normalize(quotaToastConfig) {
|
|
29
|
+
if (!quotaToastConfig)
|
|
30
|
+
return DEFAULT_CONFIG;
|
|
31
|
+
const config = {
|
|
32
|
+
enabled: typeof quotaToastConfig.enabled === "boolean"
|
|
33
|
+
? quotaToastConfig.enabled
|
|
34
|
+
: DEFAULT_CONFIG.enabled,
|
|
35
|
+
enableToast: typeof quotaToastConfig.enableToast === "boolean"
|
|
36
|
+
? quotaToastConfig.enableToast
|
|
37
|
+
: DEFAULT_CONFIG.enableToast,
|
|
38
|
+
toastStyle: quotaToastConfig.toastStyle === "grouped" || quotaToastConfig.toastStyle === "classic"
|
|
39
|
+
? quotaToastConfig.toastStyle
|
|
40
|
+
: DEFAULT_CONFIG.toastStyle,
|
|
41
|
+
minIntervalMs: typeof quotaToastConfig.minIntervalMs === "number" && quotaToastConfig.minIntervalMs > 0
|
|
42
|
+
? quotaToastConfig.minIntervalMs
|
|
43
|
+
: DEFAULT_CONFIG.minIntervalMs,
|
|
44
|
+
debug: typeof quotaToastConfig.debug === "boolean" ? quotaToastConfig.debug : DEFAULT_CONFIG.debug,
|
|
45
|
+
enabledProviders: Array.isArray(quotaToastConfig.enabledProviders)
|
|
46
|
+
? quotaToastConfig.enabledProviders.filter((p) => typeof p === "string")
|
|
47
|
+
: DEFAULT_CONFIG.enabledProviders,
|
|
48
|
+
googleModels: Array.isArray(quotaToastConfig.googleModels)
|
|
49
|
+
? quotaToastConfig.googleModels.filter(isValidGoogleModelId)
|
|
50
|
+
: DEFAULT_CONFIG.googleModels,
|
|
51
|
+
showOnIdle: typeof quotaToastConfig.showOnIdle === "boolean"
|
|
52
|
+
? quotaToastConfig.showOnIdle
|
|
53
|
+
: DEFAULT_CONFIG.showOnIdle,
|
|
54
|
+
showOnQuestion: typeof quotaToastConfig.showOnQuestion === "boolean"
|
|
55
|
+
? quotaToastConfig.showOnQuestion
|
|
56
|
+
: DEFAULT_CONFIG.showOnQuestion,
|
|
57
|
+
showOnCompact: typeof quotaToastConfig.showOnCompact === "boolean"
|
|
58
|
+
? quotaToastConfig.showOnCompact
|
|
59
|
+
: DEFAULT_CONFIG.showOnCompact,
|
|
60
|
+
showOnBothFail: typeof quotaToastConfig.showOnBothFail === "boolean"
|
|
61
|
+
? quotaToastConfig.showOnBothFail
|
|
62
|
+
: DEFAULT_CONFIG.showOnBothFail,
|
|
63
|
+
toastDurationMs: typeof quotaToastConfig.toastDurationMs === "number" && quotaToastConfig.toastDurationMs > 0
|
|
64
|
+
? quotaToastConfig.toastDurationMs
|
|
65
|
+
: DEFAULT_CONFIG.toastDurationMs,
|
|
66
|
+
onlyCurrentModel: typeof quotaToastConfig.onlyCurrentModel === "boolean"
|
|
67
|
+
? quotaToastConfig.onlyCurrentModel
|
|
68
|
+
: DEFAULT_CONFIG.onlyCurrentModel,
|
|
69
|
+
layout: {
|
|
70
|
+
maxWidth: typeof quotaToastConfig.layout?.maxWidth === "number" &&
|
|
71
|
+
quotaToastConfig.layout.maxWidth > 0
|
|
72
|
+
? quotaToastConfig.layout.maxWidth
|
|
73
|
+
: DEFAULT_CONFIG.layout.maxWidth,
|
|
74
|
+
narrowAt: typeof quotaToastConfig.layout?.narrowAt === "number" &&
|
|
75
|
+
quotaToastConfig.layout.narrowAt > 0
|
|
76
|
+
? quotaToastConfig.layout.narrowAt
|
|
77
|
+
: DEFAULT_CONFIG.layout.narrowAt,
|
|
78
|
+
tinyAt: typeof quotaToastConfig.layout?.tinyAt === "number" && quotaToastConfig.layout.tinyAt > 0
|
|
79
|
+
? quotaToastConfig.layout.tinyAt
|
|
80
|
+
: DEFAULT_CONFIG.layout.tinyAt,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
// enabledProviders is intentionally allowed to be empty (providers OFF by default).
|
|
84
|
+
// Ensure at least one Google model is configured
|
|
85
|
+
if (config.googleModels.length === 0) {
|
|
86
|
+
config.googleModels = DEFAULT_CONFIG.googleModels;
|
|
87
|
+
}
|
|
88
|
+
return config;
|
|
89
|
+
}
|
|
90
|
+
async function readJson(path) {
|
|
91
|
+
try {
|
|
92
|
+
const content = await readFile(path, "utf-8");
|
|
93
|
+
return JSON.parse(content);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function loadFromFiles() {
|
|
100
|
+
const cwd = process.cwd();
|
|
101
|
+
const configBaseDir = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
102
|
+
// Order: global first, then local overrides.
|
|
103
|
+
const candidates = [
|
|
104
|
+
join(configBaseDir, "opencode", "opencode.json"),
|
|
105
|
+
join(cwd, "opencode.json"),
|
|
106
|
+
];
|
|
107
|
+
const quota = {};
|
|
108
|
+
const usedPaths = [];
|
|
109
|
+
for (const p of candidates) {
|
|
110
|
+
if (!existsSync(p))
|
|
111
|
+
continue;
|
|
112
|
+
const parsed = await readJson(p);
|
|
113
|
+
if (!parsed || typeof parsed !== "object")
|
|
114
|
+
continue;
|
|
115
|
+
const root = parsed;
|
|
116
|
+
const picks = [
|
|
117
|
+
{ key: "experimental.quotaToast", value: root?.experimental?.quotaToast },
|
|
118
|
+
];
|
|
119
|
+
const usedKeys = [];
|
|
120
|
+
for (const pick of picks) {
|
|
121
|
+
if (!pick.value || typeof pick.value !== "object")
|
|
122
|
+
continue;
|
|
123
|
+
Object.assign(quota, pick.value);
|
|
124
|
+
usedKeys.push(pick.key);
|
|
125
|
+
}
|
|
126
|
+
if (usedKeys.length > 0) {
|
|
127
|
+
usedPaths.push(`${p} (${usedKeys.join(", ")})`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (meta) {
|
|
131
|
+
meta.source = usedPaths.length > 0 ? "files" : "defaults";
|
|
132
|
+
meta.paths = usedPaths;
|
|
133
|
+
}
|
|
134
|
+
return normalize(Object.keys(quota).length > 0 ? quota : null);
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const response = await client.config.get();
|
|
138
|
+
// OpenCode config schema is strict; plugin-specific config must live under
|
|
139
|
+
// experimental.* to avoid "unrecognized key" validation errors.
|
|
140
|
+
const quotaToastConfig = response.data?.experimental?.quotaToast;
|
|
141
|
+
if (quotaToastConfig && typeof quotaToastConfig === "object") {
|
|
142
|
+
if (meta) {
|
|
143
|
+
meta.source = "sdk";
|
|
144
|
+
meta.paths = ["client.config.get"];
|
|
145
|
+
}
|
|
146
|
+
return normalize(quotaToastConfig);
|
|
147
|
+
}
|
|
148
|
+
return await loadFromFiles();
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return await loadFromFiles();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot quota fetcher
|
|
3
|
+
*
|
|
4
|
+
* Strategy (new Copilot API reality):
|
|
5
|
+
*
|
|
6
|
+
* 1) Preferred: GitHub public billing API using a fine-grained PAT
|
|
7
|
+
* configured in ~/.config/opencode/copilot-quota-token.json.
|
|
8
|
+
* 2) Best-effort: internal endpoint using OpenCode's stored OAuth token
|
|
9
|
+
* (legacy formats or via token exchange).
|
|
10
|
+
*/
|
|
11
|
+
import type { CopilotResult } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Query GitHub Copilot premium requests quota
|
|
14
|
+
*
|
|
15
|
+
* @returns Quota result, error, or null if not configured
|
|
16
|
+
*/
|
|
17
|
+
export declare function queryCopilotQuota(): Promise<CopilotResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Format Copilot quota for toast display
|
|
20
|
+
*
|
|
21
|
+
* @param result - Copilot quota result
|
|
22
|
+
* @returns Formatted string like "Copilot 229/300 (24%)" or null
|
|
23
|
+
*/
|
|
24
|
+
export declare function formatCopilotQuota(result: CopilotResult): string | null;
|
|
25
|
+
//# sourceMappingURL=copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.d.ts","sourceRoot":"","sources":["../../src/lib/copilot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAOV,aAAa,EACd,MAAM,YAAY,CAAC;AAsSpB;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,CA2DhE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAevE"}
|