@slkiser/opencode-quota 3.2.0 → 3.4.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/README.md +278 -557
- package/dist/bin/opencode-quota.d.ts.map +1 -1
- package/dist/bin/opencode-quota.js +6 -0
- package/dist/bin/opencode-quota.js.map +1 -1
- package/dist/lib/anthropic.js +1 -1
- package/dist/lib/anthropic.js.map +1 -1
- package/dist/lib/cli-show.d.ts +8 -0
- package/dist/lib/cli-show.d.ts.map +1 -0
- package/dist/lib/cli-show.js +178 -0
- package/dist/lib/cli-show.js.map +1 -0
- package/dist/lib/config-file-utils.d.ts +13 -0
- package/dist/lib/config-file-utils.d.ts.map +1 -1
- package/dist/lib/config-file-utils.js +33 -0
- package/dist/lib/config-file-utils.js.map +1 -1
- package/dist/lib/config.d.ts +16 -3
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +452 -216
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/copilot.d.ts.map +1 -1
- package/dist/lib/copilot.js +3 -2
- package/dist/lib/copilot.js.map +1 -1
- package/dist/lib/entries.d.ts +3 -2
- package/dist/lib/entries.d.ts.map +1 -1
- package/dist/lib/format-utils.d.ts.map +1 -1
- package/dist/lib/format-utils.js +3 -2
- package/dist/lib/format-utils.js.map +1 -1
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/format.js +4 -2
- package/dist/lib/format.js.map +1 -1
- package/dist/lib/google-gemini-cli-companion.d.ts +29 -0
- package/dist/lib/google-gemini-cli-companion.d.ts.map +1 -0
- package/dist/lib/google-gemini-cli-companion.js +166 -0
- package/dist/lib/google-gemini-cli-companion.js.map +1 -0
- package/dist/lib/google-gemini-cli.d.ts +48 -0
- package/dist/lib/google-gemini-cli.d.ts.map +1 -0
- package/dist/lib/google-gemini-cli.js +447 -0
- package/dist/lib/google-gemini-cli.js.map +1 -0
- package/dist/lib/grouped-entry-normalization.js +1 -1
- package/dist/lib/grouped-entry-normalization.js.map +1 -1
- package/dist/lib/init-installer.js +2 -2
- package/dist/lib/opencode-config-providers.d.ts +6 -0
- package/dist/lib/opencode-config-providers.d.ts.map +1 -0
- package/dist/lib/opencode-config-providers.js +79 -0
- package/dist/lib/opencode-config-providers.js.map +1 -0
- package/dist/lib/opencode-go.d.ts +5 -4
- package/dist/lib/opencode-go.d.ts.map +1 -1
- package/dist/lib/opencode-go.js +32 -18
- package/dist/lib/opencode-go.js.map +1 -1
- package/dist/lib/provider-metadata.d.ts +1 -1
- package/dist/lib/provider-metadata.d.ts.map +1 -1
- package/dist/lib/provider-metadata.js +19 -0
- package/dist/lib/provider-metadata.js.map +1 -1
- package/dist/lib/provider-model-matching.d.ts +12 -0
- package/dist/lib/provider-model-matching.d.ts.map +1 -0
- package/dist/lib/provider-model-matching.js +23 -0
- package/dist/lib/provider-model-matching.js.map +1 -0
- package/dist/lib/quota-render-data.d.ts +4 -0
- package/dist/lib/quota-render-data.d.ts.map +1 -1
- package/dist/lib/quota-render-data.js +4 -1
- package/dist/lib/quota-render-data.js.map +1 -1
- package/dist/lib/quota-runtime-context.d.ts +43 -0
- package/dist/lib/quota-runtime-context.d.ts.map +1 -0
- package/dist/lib/quota-runtime-context.js +62 -0
- package/dist/lib/quota-runtime-context.js.map +1 -0
- package/dist/lib/quota-state.d.ts +1 -0
- package/dist/lib/quota-state.d.ts.map +1 -1
- package/dist/lib/quota-state.js +18 -4
- package/dist/lib/quota-state.js.map +1 -1
- package/dist/lib/quota-status.d.ts +18 -1
- package/dist/lib/quota-status.d.ts.map +1 -1
- package/dist/lib/quota-status.js +106 -22
- package/dist/lib/quota-status.js.map +1 -1
- package/dist/lib/toast-format-grouped.d.ts.map +1 -1
- package/dist/lib/toast-format-grouped.js +5 -3
- package/dist/lib/toast-format-grouped.js.map +1 -1
- package/dist/lib/tui-config-diagnostics.d.ts +7 -2
- package/dist/lib/tui-config-diagnostics.d.ts.map +1 -1
- package/dist/lib/tui-config-diagnostics.js +27 -8
- package/dist/lib/tui-config-diagnostics.js.map +1 -1
- package/dist/lib/tui-runtime.d.ts.map +1 -1
- package/dist/lib/tui-runtime.js +25 -16
- package/dist/lib/tui-runtime.js.map +1 -1
- package/dist/lib/types.d.ts +55 -9
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +423 -159
- package/dist/plugin.js.map +1 -1
- package/dist/providers/chutes.d.ts.map +1 -1
- package/dist/providers/chutes.js +2 -4
- package/dist/providers/chutes.js.map +1 -1
- package/dist/providers/copilot.d.ts.map +1 -1
- package/dist/providers/copilot.js +5 -10
- package/dist/providers/copilot.js.map +1 -1
- package/dist/providers/cursor.js +2 -2
- package/dist/providers/cursor.js.map +1 -1
- package/dist/providers/google-account-format.d.ts +6 -0
- package/dist/providers/google-account-format.d.ts.map +1 -0
- package/dist/providers/google-account-format.js +21 -0
- package/dist/providers/google-account-format.js.map +1 -0
- package/dist/providers/google-antigravity.d.ts.map +1 -1
- package/dist/providers/google-antigravity.js +7 -29
- package/dist/providers/google-antigravity.js.map +1 -1
- package/dist/providers/google-gemini-cli.d.ts +3 -0
- package/dist/providers/google-gemini-cli.d.ts.map +1 -0
- package/dist/providers/google-gemini-cli.js +63 -0
- package/dist/providers/google-gemini-cli.js.map +1 -0
- package/dist/providers/minimax-coding-plan.js +2 -2
- package/dist/providers/minimax-coding-plan.js.map +1 -1
- package/dist/providers/nanogpt.d.ts.map +1 -1
- package/dist/providers/nanogpt.js +2 -2
- package/dist/providers/nanogpt.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +2 -4
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/opencode-go.d.ts +2 -2
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +60 -11
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +2 -0
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/synthetic.d.ts.map +1 -1
- package/dist/providers/synthetic.js +2 -4
- package/dist/providers/synthetic.js.map +1 -1
- package/package.json +2 -1
package/dist/lib/config.js
CHANGED
|
@@ -3,25 +3,63 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Precedence model:
|
|
5
5
|
* - Global/user config provides defaults.
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* - SDK config is used only as a fallback when no config files are found.
|
|
6
|
+
* - Workspace config at the resolved config root overrides ordinary settings.
|
|
7
|
+
* - SDK config is used only as a fallback when no file-backed config exists.
|
|
9
8
|
*/
|
|
10
9
|
import { DEFAULT_CONFIG } from "./types.js";
|
|
11
10
|
import { isQuotaFormatStyle, resolveQuotaFormatStyle } from "./quota-format-style.js";
|
|
12
11
|
import { parseJsonOrJsonc } from "./jsonc.js";
|
|
13
|
-
import { normalizeQuotaProviderId } from "./provider-metadata.js";
|
|
12
|
+
import { getQuotaProviderShape, normalizeQuotaProviderId } from "./provider-metadata.js";
|
|
14
13
|
import { existsSync } from "fs";
|
|
15
14
|
import { readFile } from "fs/promises";
|
|
16
15
|
import { join } from "path";
|
|
17
16
|
import { getOpencodeRuntimeDirCandidates } from "./opencode-runtime-paths.js";
|
|
17
|
+
export const QUOTA_TOAST_SETTING_SOURCE_KEYS = [
|
|
18
|
+
"enabled",
|
|
19
|
+
"enableToast",
|
|
20
|
+
"formatStyle",
|
|
21
|
+
"percentDisplayMode",
|
|
22
|
+
"minIntervalMs",
|
|
23
|
+
"debug",
|
|
24
|
+
"enabledProviders",
|
|
25
|
+
"anthropicBinaryPath",
|
|
26
|
+
"googleModels",
|
|
27
|
+
"alibabaCodingPlanTier",
|
|
28
|
+
"cursorPlan",
|
|
29
|
+
"cursorIncludedApiUsd",
|
|
30
|
+
"cursorBillingCycleStartDay",
|
|
31
|
+
"opencodeGoWindows",
|
|
32
|
+
"pricingSnapshot.source",
|
|
33
|
+
"pricingSnapshot.autoRefresh",
|
|
34
|
+
"showOnIdle",
|
|
35
|
+
"showOnQuestion",
|
|
36
|
+
"showOnCompact",
|
|
37
|
+
"showOnBothFail",
|
|
38
|
+
"toastDurationMs",
|
|
39
|
+
"onlyCurrentModel",
|
|
40
|
+
"showSessionTokens",
|
|
41
|
+
"layout.maxWidth",
|
|
42
|
+
"layout.narrowAt",
|
|
43
|
+
"layout.tinyAt",
|
|
44
|
+
];
|
|
18
45
|
export function createLoadConfigMeta() {
|
|
19
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
source: "defaults",
|
|
48
|
+
paths: [],
|
|
49
|
+
globalConfigPaths: [],
|
|
50
|
+
workspaceConfigPaths: [],
|
|
51
|
+
settingSources: {},
|
|
52
|
+
networkSettingSources: {},
|
|
53
|
+
configIssues: [],
|
|
54
|
+
};
|
|
20
55
|
}
|
|
21
|
-
const
|
|
56
|
+
const CONFIG_FILENAMES = ["opencode.json", "opencode.jsonc"];
|
|
57
|
+
const NETWORK_SETTING_SOURCE_KEYS = [
|
|
22
58
|
"enabled",
|
|
23
59
|
"enabledProviders",
|
|
24
60
|
"minIntervalMs",
|
|
61
|
+
"pricingSnapshot.source",
|
|
62
|
+
"pricingSnapshot.autoRefresh",
|
|
25
63
|
"showOnIdle",
|
|
26
64
|
"showOnQuestion",
|
|
27
65
|
"showOnCompact",
|
|
@@ -40,8 +78,7 @@ function isValidGoogleModelId(id) {
|
|
|
40
78
|
return typeof id === "string" && ["G3PRO", "G3FLASH", "CLAUDE", "G3IMAGE"].includes(id);
|
|
41
79
|
}
|
|
42
80
|
function isValidCursorQuotaPlan(plan) {
|
|
43
|
-
return (typeof plan === "string" &&
|
|
44
|
-
["none", "pro", "pro-plus", "ultra"].includes(plan));
|
|
81
|
+
return (typeof plan === "string" && ["none", "pro", "pro-plus", "ultra"].includes(plan));
|
|
45
82
|
}
|
|
46
83
|
function isValidPricingSnapshotSource(source) {
|
|
47
84
|
return typeof source === "string" && ["auto", "bundled", "runtime"].includes(source);
|
|
@@ -52,6 +89,23 @@ function isValidPricingSnapshotAutoRefresh(value) {
|
|
|
52
89
|
function isValidPercentDisplayMode(value) {
|
|
53
90
|
return value === "remaining" || value === "used";
|
|
54
91
|
}
|
|
92
|
+
function isValidAlibabaCodingPlanTier(value) {
|
|
93
|
+
return value === "lite" || value === "pro";
|
|
94
|
+
}
|
|
95
|
+
function isPositiveNumber(value) {
|
|
96
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
97
|
+
}
|
|
98
|
+
function isValidCursorBillingCycleStartDay(value) {
|
|
99
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 28;
|
|
100
|
+
}
|
|
101
|
+
const VALID_OPENCODE_GO_WINDOWS = ["rolling", "weekly", "monthly"];
|
|
102
|
+
function isValidOpenCodeGoWindows(value) {
|
|
103
|
+
if (!Array.isArray(value))
|
|
104
|
+
return false;
|
|
105
|
+
if (value.length === 0)
|
|
106
|
+
return false;
|
|
107
|
+
return value.every((v) => typeof v === "string" && VALID_OPENCODE_GO_WINDOWS.includes(v));
|
|
108
|
+
}
|
|
55
109
|
function normalizeOptionalString(value) {
|
|
56
110
|
if (typeof value !== "string") {
|
|
57
111
|
return undefined;
|
|
@@ -78,53 +132,340 @@ function getConfiguredFormatStyle(quotaToastConfig) {
|
|
|
78
132
|
function dedupe(list) {
|
|
79
133
|
return [...new Set(list)];
|
|
80
134
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
135
|
+
function cloneDefaultConfig() {
|
|
136
|
+
return {
|
|
137
|
+
...DEFAULT_CONFIG,
|
|
138
|
+
enabledProviders: Array.isArray(DEFAULT_CONFIG.enabledProviders)
|
|
139
|
+
? [...DEFAULT_CONFIG.enabledProviders]
|
|
140
|
+
: DEFAULT_CONFIG.enabledProviders,
|
|
141
|
+
googleModels: [...DEFAULT_CONFIG.googleModels],
|
|
142
|
+
opencodeGoWindows: [...DEFAULT_CONFIG.opencodeGoWindows],
|
|
143
|
+
pricingSnapshot: { ...DEFAULT_CONFIG.pricingSnapshot },
|
|
144
|
+
layout: { ...DEFAULT_CONFIG.layout },
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function describeInvalidProviderValue(value) {
|
|
148
|
+
return typeof value === "string" ? value : typeof value;
|
|
149
|
+
}
|
|
150
|
+
function normalizeEnabledProviders(value) {
|
|
151
|
+
if (value === "auto") {
|
|
152
|
+
return { value: "auto", issues: [] };
|
|
153
|
+
}
|
|
154
|
+
if (!Array.isArray(value)) {
|
|
155
|
+
return {
|
|
156
|
+
value: [],
|
|
157
|
+
issues: ["expected \"auto\" or an array of provider ids"],
|
|
158
|
+
invalidEmpty: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (value.length === 0) {
|
|
162
|
+
return { value: [], issues: [] };
|
|
163
|
+
}
|
|
164
|
+
const validProviders = [];
|
|
165
|
+
const invalidProviders = [];
|
|
166
|
+
for (const provider of value) {
|
|
167
|
+
if (typeof provider !== "string") {
|
|
168
|
+
invalidProviders.push(describeInvalidProviderValue(provider));
|
|
169
|
+
continue;
|
|
97
170
|
}
|
|
98
|
-
|
|
99
|
-
|
|
171
|
+
const normalized = normalizeQuotaProviderId(provider);
|
|
172
|
+
if (normalized && getQuotaProviderShape(normalized)) {
|
|
173
|
+
validProviders.push(normalized);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
invalidProviders.push(provider);
|
|
100
177
|
}
|
|
101
178
|
}
|
|
102
|
-
|
|
179
|
+
const issues = invalidProviders.length
|
|
180
|
+
? [`unknown provider id(s): ${dedupe(invalidProviders).join(", ")}`]
|
|
181
|
+
: [];
|
|
182
|
+
const normalizedProviders = dedupe(validProviders);
|
|
183
|
+
return {
|
|
184
|
+
value: normalizedProviders,
|
|
185
|
+
issues,
|
|
186
|
+
invalidEmpty: normalizedProviders.length === 0 && invalidProviders.length > 0,
|
|
187
|
+
};
|
|
103
188
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
189
|
+
function normalizeGoogleModels(value) {
|
|
190
|
+
if (!Array.isArray(value)) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
const models = value.filter(isValidGoogleModelId);
|
|
194
|
+
return models.length > 0 ? models : undefined;
|
|
195
|
+
}
|
|
196
|
+
function extractPricingSnapshotPatch(value) {
|
|
197
|
+
if (!isPlainObject(value)) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
const patch = {};
|
|
201
|
+
if (hasOwnKey(value, "source") && isValidPricingSnapshotSource(value.source)) {
|
|
202
|
+
patch.source = value.source;
|
|
203
|
+
}
|
|
204
|
+
if (hasOwnKey(value, "autoRefresh") && isValidPricingSnapshotAutoRefresh(value.autoRefresh)) {
|
|
205
|
+
patch.autoRefresh = value.autoRefresh;
|
|
206
|
+
}
|
|
207
|
+
return Object.keys(patch).length > 0 ? patch : undefined;
|
|
208
|
+
}
|
|
209
|
+
function extractLayoutPatch(value) {
|
|
210
|
+
if (!isPlainObject(value)) {
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const patch = {};
|
|
214
|
+
if (hasOwnKey(value, "maxWidth") && isPositiveNumber(value.maxWidth)) {
|
|
215
|
+
patch.maxWidth = value.maxWidth;
|
|
216
|
+
}
|
|
217
|
+
if (hasOwnKey(value, "narrowAt") && isPositiveNumber(value.narrowAt)) {
|
|
218
|
+
patch.narrowAt = value.narrowAt;
|
|
219
|
+
}
|
|
220
|
+
if (hasOwnKey(value, "tinyAt") && isPositiveNumber(value.tinyAt)) {
|
|
221
|
+
patch.tinyAt = value.tinyAt;
|
|
222
|
+
}
|
|
223
|
+
return Object.keys(patch).length > 0 ? patch : undefined;
|
|
224
|
+
}
|
|
225
|
+
function extractValidatedQuotaToastPatch(quotaToastConfig, reportIssue) {
|
|
226
|
+
const patch = {};
|
|
227
|
+
if (hasOwnKey(quotaToastConfig, "enabled") && typeof quotaToastConfig.enabled === "boolean") {
|
|
228
|
+
patch.enabled = quotaToastConfig.enabled;
|
|
229
|
+
}
|
|
230
|
+
if (hasOwnKey(quotaToastConfig, "enableToast") &&
|
|
231
|
+
typeof quotaToastConfig.enableToast === "boolean") {
|
|
232
|
+
patch.enableToast = quotaToastConfig.enableToast;
|
|
233
|
+
}
|
|
234
|
+
const formatStyle = getConfiguredFormatStyle(quotaToastConfig);
|
|
235
|
+
if (formatStyle) {
|
|
236
|
+
patch.formatStyle = formatStyle;
|
|
237
|
+
}
|
|
238
|
+
if (hasOwnKey(quotaToastConfig, "percentDisplayMode") &&
|
|
239
|
+
isValidPercentDisplayMode(quotaToastConfig.percentDisplayMode)) {
|
|
240
|
+
patch.percentDisplayMode = quotaToastConfig.percentDisplayMode;
|
|
241
|
+
}
|
|
242
|
+
if (hasOwnKey(quotaToastConfig, "minIntervalMs") && isPositiveNumber(quotaToastConfig.minIntervalMs)) {
|
|
243
|
+
patch.minIntervalMs = quotaToastConfig.minIntervalMs;
|
|
244
|
+
}
|
|
245
|
+
if (hasOwnKey(quotaToastConfig, "debug") && typeof quotaToastConfig.debug === "boolean") {
|
|
246
|
+
patch.debug = quotaToastConfig.debug;
|
|
247
|
+
}
|
|
248
|
+
if (hasOwnKey(quotaToastConfig, "enabledProviders")) {
|
|
249
|
+
const enabledProviders = normalizeEnabledProviders(quotaToastConfig.enabledProviders);
|
|
250
|
+
for (const issue of enabledProviders.issues) {
|
|
251
|
+
reportIssue?.("enabledProviders", issue);
|
|
252
|
+
}
|
|
253
|
+
if (enabledProviders.value !== undefined) {
|
|
254
|
+
patch.enabledProviders = enabledProviders.value;
|
|
255
|
+
if (enabledProviders.invalidEmpty) {
|
|
256
|
+
patch.enabledProvidersInvalidEmpty = true;
|
|
257
|
+
}
|
|
116
258
|
}
|
|
117
259
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
sources["pricingSnapshot.source"] = sourcePath;
|
|
260
|
+
if (hasOwnKey(quotaToastConfig, "anthropicBinaryPath")) {
|
|
261
|
+
const anthropicBinaryPath = normalizeOptionalString(quotaToastConfig.anthropicBinaryPath);
|
|
262
|
+
if (anthropicBinaryPath !== undefined) {
|
|
263
|
+
patch.anthropicBinaryPath = anthropicBinaryPath;
|
|
123
264
|
}
|
|
124
|
-
|
|
125
|
-
|
|
265
|
+
}
|
|
266
|
+
if (hasOwnKey(quotaToastConfig, "googleModels")) {
|
|
267
|
+
const googleModels = normalizeGoogleModels(quotaToastConfig.googleModels);
|
|
268
|
+
if (googleModels !== undefined) {
|
|
269
|
+
patch.googleModels = googleModels;
|
|
126
270
|
}
|
|
127
271
|
}
|
|
272
|
+
if (hasOwnKey(quotaToastConfig, "alibabaCodingPlanTier") &&
|
|
273
|
+
isValidAlibabaCodingPlanTier(quotaToastConfig.alibabaCodingPlanTier)) {
|
|
274
|
+
patch.alibabaCodingPlanTier = quotaToastConfig.alibabaCodingPlanTier;
|
|
275
|
+
}
|
|
276
|
+
if (hasOwnKey(quotaToastConfig, "cursorPlan") && isValidCursorQuotaPlan(quotaToastConfig.cursorPlan)) {
|
|
277
|
+
patch.cursorPlan = quotaToastConfig.cursorPlan;
|
|
278
|
+
}
|
|
279
|
+
if (hasOwnKey(quotaToastConfig, "cursorIncludedApiUsd") &&
|
|
280
|
+
isPositiveNumber(quotaToastConfig.cursorIncludedApiUsd)) {
|
|
281
|
+
patch.cursorIncludedApiUsd = quotaToastConfig.cursorIncludedApiUsd;
|
|
282
|
+
}
|
|
283
|
+
if (hasOwnKey(quotaToastConfig, "cursorBillingCycleStartDay") &&
|
|
284
|
+
isValidCursorBillingCycleStartDay(quotaToastConfig.cursorBillingCycleStartDay)) {
|
|
285
|
+
patch.cursorBillingCycleStartDay = quotaToastConfig.cursorBillingCycleStartDay;
|
|
286
|
+
}
|
|
287
|
+
if (hasOwnKey(quotaToastConfig, "opencodeGoWindows") &&
|
|
288
|
+
isValidOpenCodeGoWindows(quotaToastConfig.opencodeGoWindows)) {
|
|
289
|
+
patch.opencodeGoWindows = quotaToastConfig.opencodeGoWindows;
|
|
290
|
+
}
|
|
291
|
+
if (hasOwnKey(quotaToastConfig, "pricingSnapshot")) {
|
|
292
|
+
const pricingSnapshot = extractPricingSnapshotPatch(quotaToastConfig.pricingSnapshot);
|
|
293
|
+
if (pricingSnapshot) {
|
|
294
|
+
patch.pricingSnapshot = pricingSnapshot;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (hasOwnKey(quotaToastConfig, "showOnIdle") && typeof quotaToastConfig.showOnIdle === "boolean") {
|
|
298
|
+
patch.showOnIdle = quotaToastConfig.showOnIdle;
|
|
299
|
+
}
|
|
300
|
+
if (hasOwnKey(quotaToastConfig, "showOnQuestion") &&
|
|
301
|
+
typeof quotaToastConfig.showOnQuestion === "boolean") {
|
|
302
|
+
patch.showOnQuestion = quotaToastConfig.showOnQuestion;
|
|
303
|
+
}
|
|
304
|
+
if (hasOwnKey(quotaToastConfig, "showOnCompact") &&
|
|
305
|
+
typeof quotaToastConfig.showOnCompact === "boolean") {
|
|
306
|
+
patch.showOnCompact = quotaToastConfig.showOnCompact;
|
|
307
|
+
}
|
|
308
|
+
if (hasOwnKey(quotaToastConfig, "showOnBothFail") &&
|
|
309
|
+
typeof quotaToastConfig.showOnBothFail === "boolean") {
|
|
310
|
+
patch.showOnBothFail = quotaToastConfig.showOnBothFail;
|
|
311
|
+
}
|
|
312
|
+
if (hasOwnKey(quotaToastConfig, "toastDurationMs") &&
|
|
313
|
+
isPositiveNumber(quotaToastConfig.toastDurationMs)) {
|
|
314
|
+
patch.toastDurationMs = quotaToastConfig.toastDurationMs;
|
|
315
|
+
}
|
|
316
|
+
if (hasOwnKey(quotaToastConfig, "onlyCurrentModel") &&
|
|
317
|
+
typeof quotaToastConfig.onlyCurrentModel === "boolean") {
|
|
318
|
+
patch.onlyCurrentModel = quotaToastConfig.onlyCurrentModel;
|
|
319
|
+
}
|
|
320
|
+
if (hasOwnKey(quotaToastConfig, "showSessionTokens") &&
|
|
321
|
+
typeof quotaToastConfig.showSessionTokens === "boolean") {
|
|
322
|
+
patch.showSessionTokens = quotaToastConfig.showSessionTokens;
|
|
323
|
+
}
|
|
324
|
+
if (hasOwnKey(quotaToastConfig, "layout")) {
|
|
325
|
+
const layout = extractLayoutPatch(quotaToastConfig.layout);
|
|
326
|
+
if (layout) {
|
|
327
|
+
patch.layout = layout;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return patch;
|
|
331
|
+
}
|
|
332
|
+
function applySettingSource(settingSources, key, sourcePath) {
|
|
333
|
+
settingSources[key] = sourcePath;
|
|
334
|
+
}
|
|
335
|
+
function applyValidatedQuotaToastPatch(config, patch, sourcePath, settingSources) {
|
|
336
|
+
if (hasOwnKey(patch, "enabled")) {
|
|
337
|
+
config.enabled = patch.enabled;
|
|
338
|
+
applySettingSource(settingSources, "enabled", sourcePath);
|
|
339
|
+
}
|
|
340
|
+
if (hasOwnKey(patch, "enableToast")) {
|
|
341
|
+
config.enableToast = patch.enableToast;
|
|
342
|
+
applySettingSource(settingSources, "enableToast", sourcePath);
|
|
343
|
+
}
|
|
344
|
+
if (hasOwnKey(patch, "formatStyle")) {
|
|
345
|
+
config.formatStyle = patch.formatStyle;
|
|
346
|
+
applySettingSource(settingSources, "formatStyle", sourcePath);
|
|
347
|
+
}
|
|
348
|
+
if (hasOwnKey(patch, "percentDisplayMode")) {
|
|
349
|
+
config.percentDisplayMode = patch.percentDisplayMode;
|
|
350
|
+
applySettingSource(settingSources, "percentDisplayMode", sourcePath);
|
|
351
|
+
}
|
|
352
|
+
if (hasOwnKey(patch, "minIntervalMs")) {
|
|
353
|
+
config.minIntervalMs = patch.minIntervalMs;
|
|
354
|
+
applySettingSource(settingSources, "minIntervalMs", sourcePath);
|
|
355
|
+
}
|
|
356
|
+
if (hasOwnKey(patch, "debug")) {
|
|
357
|
+
config.debug = patch.debug;
|
|
358
|
+
applySettingSource(settingSources, "debug", sourcePath);
|
|
359
|
+
}
|
|
360
|
+
if (hasOwnKey(patch, "enabledProviders")) {
|
|
361
|
+
if (!(patch.enabledProvidersInvalidEmpty && settingSources.enabledProviders)) {
|
|
362
|
+
config.enabledProviders =
|
|
363
|
+
patch.enabledProviders === "auto" ? "auto" : [...patch.enabledProviders];
|
|
364
|
+
applySettingSource(settingSources, "enabledProviders", sourcePath);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (hasOwnKey(patch, "anthropicBinaryPath")) {
|
|
368
|
+
config.anthropicBinaryPath = patch.anthropicBinaryPath;
|
|
369
|
+
applySettingSource(settingSources, "anthropicBinaryPath", sourcePath);
|
|
370
|
+
}
|
|
371
|
+
if (hasOwnKey(patch, "googleModels")) {
|
|
372
|
+
config.googleModels = [...patch.googleModels];
|
|
373
|
+
applySettingSource(settingSources, "googleModels", sourcePath);
|
|
374
|
+
}
|
|
375
|
+
if (hasOwnKey(patch, "alibabaCodingPlanTier")) {
|
|
376
|
+
config.alibabaCodingPlanTier = patch.alibabaCodingPlanTier;
|
|
377
|
+
applySettingSource(settingSources, "alibabaCodingPlanTier", sourcePath);
|
|
378
|
+
}
|
|
379
|
+
if (hasOwnKey(patch, "cursorPlan")) {
|
|
380
|
+
config.cursorPlan = patch.cursorPlan;
|
|
381
|
+
applySettingSource(settingSources, "cursorPlan", sourcePath);
|
|
382
|
+
}
|
|
383
|
+
if (hasOwnKey(patch, "cursorIncludedApiUsd")) {
|
|
384
|
+
config.cursorIncludedApiUsd = patch.cursorIncludedApiUsd;
|
|
385
|
+
applySettingSource(settingSources, "cursorIncludedApiUsd", sourcePath);
|
|
386
|
+
}
|
|
387
|
+
if (hasOwnKey(patch, "cursorBillingCycleStartDay")) {
|
|
388
|
+
config.cursorBillingCycleStartDay = patch.cursorBillingCycleStartDay;
|
|
389
|
+
applySettingSource(settingSources, "cursorBillingCycleStartDay", sourcePath);
|
|
390
|
+
}
|
|
391
|
+
if (hasOwnKey(patch, "opencodeGoWindows")) {
|
|
392
|
+
config.opencodeGoWindows = [...patch.opencodeGoWindows];
|
|
393
|
+
applySettingSource(settingSources, "opencodeGoWindows", sourcePath);
|
|
394
|
+
}
|
|
395
|
+
if (patch.pricingSnapshot) {
|
|
396
|
+
if (hasOwnKey(patch.pricingSnapshot, "source")) {
|
|
397
|
+
config.pricingSnapshot.source = patch.pricingSnapshot.source;
|
|
398
|
+
applySettingSource(settingSources, "pricingSnapshot.source", sourcePath);
|
|
399
|
+
}
|
|
400
|
+
if (hasOwnKey(patch.pricingSnapshot, "autoRefresh")) {
|
|
401
|
+
config.pricingSnapshot.autoRefresh = patch.pricingSnapshot.autoRefresh;
|
|
402
|
+
applySettingSource(settingSources, "pricingSnapshot.autoRefresh", sourcePath);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (hasOwnKey(patch, "showOnIdle")) {
|
|
406
|
+
config.showOnIdle = patch.showOnIdle;
|
|
407
|
+
applySettingSource(settingSources, "showOnIdle", sourcePath);
|
|
408
|
+
}
|
|
409
|
+
if (hasOwnKey(patch, "showOnQuestion")) {
|
|
410
|
+
config.showOnQuestion = patch.showOnQuestion;
|
|
411
|
+
applySettingSource(settingSources, "showOnQuestion", sourcePath);
|
|
412
|
+
}
|
|
413
|
+
if (hasOwnKey(patch, "showOnCompact")) {
|
|
414
|
+
config.showOnCompact = patch.showOnCompact;
|
|
415
|
+
applySettingSource(settingSources, "showOnCompact", sourcePath);
|
|
416
|
+
}
|
|
417
|
+
if (hasOwnKey(patch, "showOnBothFail")) {
|
|
418
|
+
config.showOnBothFail = patch.showOnBothFail;
|
|
419
|
+
applySettingSource(settingSources, "showOnBothFail", sourcePath);
|
|
420
|
+
}
|
|
421
|
+
if (hasOwnKey(patch, "toastDurationMs")) {
|
|
422
|
+
config.toastDurationMs = patch.toastDurationMs;
|
|
423
|
+
applySettingSource(settingSources, "toastDurationMs", sourcePath);
|
|
424
|
+
}
|
|
425
|
+
if (hasOwnKey(patch, "onlyCurrentModel")) {
|
|
426
|
+
config.onlyCurrentModel = patch.onlyCurrentModel;
|
|
427
|
+
applySettingSource(settingSources, "onlyCurrentModel", sourcePath);
|
|
428
|
+
}
|
|
429
|
+
if (hasOwnKey(patch, "showSessionTokens")) {
|
|
430
|
+
config.showSessionTokens = patch.showSessionTokens;
|
|
431
|
+
applySettingSource(settingSources, "showSessionTokens", sourcePath);
|
|
432
|
+
}
|
|
433
|
+
if (patch.layout) {
|
|
434
|
+
if (hasOwnKey(patch.layout, "maxWidth")) {
|
|
435
|
+
config.layout.maxWidth = patch.layout.maxWidth;
|
|
436
|
+
applySettingSource(settingSources, "layout.maxWidth", sourcePath);
|
|
437
|
+
}
|
|
438
|
+
if (hasOwnKey(patch.layout, "narrowAt")) {
|
|
439
|
+
config.layout.narrowAt = patch.layout.narrowAt;
|
|
440
|
+
applySettingSource(settingSources, "layout.narrowAt", sourcePath);
|
|
441
|
+
}
|
|
442
|
+
if (hasOwnKey(patch.layout, "tinyAt")) {
|
|
443
|
+
config.layout.tinyAt = patch.layout.tinyAt;
|
|
444
|
+
applySettingSource(settingSources, "layout.tinyAt", sourcePath);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function projectNetworkSettingSources(settingSources) {
|
|
449
|
+
const projected = {};
|
|
450
|
+
for (const key of NETWORK_SETTING_SOURCE_KEYS) {
|
|
451
|
+
const source = settingSources[key];
|
|
452
|
+
if (typeof source === "string" && source.length > 0) {
|
|
453
|
+
projected[key] = source;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return projected;
|
|
457
|
+
}
|
|
458
|
+
function buildConfigLayerCandidates(configDirs, configRootDir) {
|
|
459
|
+
const workspaceCandidates = CONFIG_FILENAMES.map((filename) => ({
|
|
460
|
+
path: join(configRootDir, filename),
|
|
461
|
+
scope: "workspace",
|
|
462
|
+
}));
|
|
463
|
+
const workspacePaths = new Set(workspaceCandidates.map((candidate) => candidate.path));
|
|
464
|
+
const globalCandidates = configDirs.flatMap((dir) => CONFIG_FILENAMES.map((filename) => ({ path: join(dir, filename), scope: "global" })));
|
|
465
|
+
return [
|
|
466
|
+
...globalCandidates.filter((candidate) => !workspacePaths.has(candidate.path)),
|
|
467
|
+
...workspaceCandidates,
|
|
468
|
+
];
|
|
128
469
|
}
|
|
129
470
|
/**
|
|
130
471
|
* Load plugin configuration from OpenCode config
|
|
@@ -133,106 +474,6 @@ function recordNetworkSettingSource(sources, quotaToast, sourcePath) {
|
|
|
133
474
|
* @returns Merged configuration with defaults
|
|
134
475
|
*/
|
|
135
476
|
export async function loadConfig(client, meta, options) {
|
|
136
|
-
function normalize(quotaToastConfig) {
|
|
137
|
-
if (!quotaToastConfig)
|
|
138
|
-
return DEFAULT_CONFIG;
|
|
139
|
-
const formatStyle = getConfiguredFormatStyle(quotaToastConfig) ?? DEFAULT_CONFIG.formatStyle;
|
|
140
|
-
const config = {
|
|
141
|
-
enabled: typeof quotaToastConfig.enabled === "boolean"
|
|
142
|
-
? quotaToastConfig.enabled
|
|
143
|
-
: DEFAULT_CONFIG.enabled,
|
|
144
|
-
enableToast: typeof quotaToastConfig.enableToast === "boolean"
|
|
145
|
-
? quotaToastConfig.enableToast
|
|
146
|
-
: DEFAULT_CONFIG.enableToast,
|
|
147
|
-
formatStyle,
|
|
148
|
-
percentDisplayMode: isValidPercentDisplayMode(quotaToastConfig.percentDisplayMode)
|
|
149
|
-
? quotaToastConfig.percentDisplayMode
|
|
150
|
-
: DEFAULT_CONFIG.percentDisplayMode,
|
|
151
|
-
minIntervalMs: typeof quotaToastConfig.minIntervalMs === "number" && quotaToastConfig.minIntervalMs > 0
|
|
152
|
-
? quotaToastConfig.minIntervalMs
|
|
153
|
-
: DEFAULT_CONFIG.minIntervalMs,
|
|
154
|
-
debug: typeof quotaToastConfig.debug === "boolean" ? quotaToastConfig.debug : DEFAULT_CONFIG.debug,
|
|
155
|
-
enabledProviders: quotaToastConfig.enabledProviders === "auto"
|
|
156
|
-
? "auto"
|
|
157
|
-
: Array.isArray(quotaToastConfig.enabledProviders)
|
|
158
|
-
? dedupe(quotaToastConfig.enabledProviders
|
|
159
|
-
.filter((p) => typeof p === "string")
|
|
160
|
-
.map(normalizeQuotaProviderId)
|
|
161
|
-
.filter(Boolean))
|
|
162
|
-
: DEFAULT_CONFIG.enabledProviders,
|
|
163
|
-
anthropicBinaryPath: normalizeOptionalString(quotaToastConfig.anthropicBinaryPath) ??
|
|
164
|
-
DEFAULT_CONFIG.anthropicBinaryPath,
|
|
165
|
-
googleModels: Array.isArray(quotaToastConfig.googleModels)
|
|
166
|
-
? quotaToastConfig.googleModels.filter(isValidGoogleModelId)
|
|
167
|
-
: DEFAULT_CONFIG.googleModels,
|
|
168
|
-
alibabaCodingPlanTier: quotaToastConfig.alibabaCodingPlanTier === "lite" ||
|
|
169
|
-
quotaToastConfig.alibabaCodingPlanTier === "pro"
|
|
170
|
-
? quotaToastConfig.alibabaCodingPlanTier
|
|
171
|
-
: DEFAULT_CONFIG.alibabaCodingPlanTier,
|
|
172
|
-
cursorPlan: isValidCursorQuotaPlan(quotaToastConfig.cursorPlan)
|
|
173
|
-
? quotaToastConfig.cursorPlan
|
|
174
|
-
: DEFAULT_CONFIG.cursorPlan,
|
|
175
|
-
cursorIncludedApiUsd: typeof quotaToastConfig.cursorIncludedApiUsd === "number" &&
|
|
176
|
-
Number.isFinite(quotaToastConfig.cursorIncludedApiUsd) &&
|
|
177
|
-
quotaToastConfig.cursorIncludedApiUsd > 0
|
|
178
|
-
? quotaToastConfig.cursorIncludedApiUsd
|
|
179
|
-
: undefined,
|
|
180
|
-
cursorBillingCycleStartDay: typeof quotaToastConfig.cursorBillingCycleStartDay === "number" &&
|
|
181
|
-
Number.isInteger(quotaToastConfig.cursorBillingCycleStartDay) &&
|
|
182
|
-
quotaToastConfig.cursorBillingCycleStartDay >= 1 &&
|
|
183
|
-
quotaToastConfig.cursorBillingCycleStartDay <= 28
|
|
184
|
-
? quotaToastConfig.cursorBillingCycleStartDay
|
|
185
|
-
: undefined,
|
|
186
|
-
pricingSnapshot: {
|
|
187
|
-
source: isValidPricingSnapshotSource(quotaToastConfig.pricingSnapshot?.source)
|
|
188
|
-
? quotaToastConfig.pricingSnapshot.source
|
|
189
|
-
: DEFAULT_CONFIG.pricingSnapshot.source,
|
|
190
|
-
autoRefresh: isValidPricingSnapshotAutoRefresh(quotaToastConfig.pricingSnapshot?.autoRefresh)
|
|
191
|
-
? quotaToastConfig.pricingSnapshot.autoRefresh
|
|
192
|
-
: DEFAULT_CONFIG.pricingSnapshot.autoRefresh,
|
|
193
|
-
},
|
|
194
|
-
showOnIdle: typeof quotaToastConfig.showOnIdle === "boolean"
|
|
195
|
-
? quotaToastConfig.showOnIdle
|
|
196
|
-
: DEFAULT_CONFIG.showOnIdle,
|
|
197
|
-
showOnQuestion: typeof quotaToastConfig.showOnQuestion === "boolean"
|
|
198
|
-
? quotaToastConfig.showOnQuestion
|
|
199
|
-
: DEFAULT_CONFIG.showOnQuestion,
|
|
200
|
-
showOnCompact: typeof quotaToastConfig.showOnCompact === "boolean"
|
|
201
|
-
? quotaToastConfig.showOnCompact
|
|
202
|
-
: DEFAULT_CONFIG.showOnCompact,
|
|
203
|
-
showOnBothFail: typeof quotaToastConfig.showOnBothFail === "boolean"
|
|
204
|
-
? quotaToastConfig.showOnBothFail
|
|
205
|
-
: DEFAULT_CONFIG.showOnBothFail,
|
|
206
|
-
toastDurationMs: typeof quotaToastConfig.toastDurationMs === "number" && quotaToastConfig.toastDurationMs > 0
|
|
207
|
-
? quotaToastConfig.toastDurationMs
|
|
208
|
-
: DEFAULT_CONFIG.toastDurationMs,
|
|
209
|
-
onlyCurrentModel: typeof quotaToastConfig.onlyCurrentModel === "boolean"
|
|
210
|
-
? quotaToastConfig.onlyCurrentModel
|
|
211
|
-
: DEFAULT_CONFIG.onlyCurrentModel,
|
|
212
|
-
showSessionTokens: typeof quotaToastConfig.showSessionTokens === "boolean"
|
|
213
|
-
? quotaToastConfig.showSessionTokens
|
|
214
|
-
: DEFAULT_CONFIG.showSessionTokens,
|
|
215
|
-
layout: {
|
|
216
|
-
maxWidth: typeof quotaToastConfig.layout?.maxWidth === "number" &&
|
|
217
|
-
quotaToastConfig.layout.maxWidth > 0
|
|
218
|
-
? quotaToastConfig.layout.maxWidth
|
|
219
|
-
: DEFAULT_CONFIG.layout.maxWidth,
|
|
220
|
-
narrowAt: typeof quotaToastConfig.layout?.narrowAt === "number" &&
|
|
221
|
-
quotaToastConfig.layout.narrowAt > 0
|
|
222
|
-
? quotaToastConfig.layout.narrowAt
|
|
223
|
-
: DEFAULT_CONFIG.layout.narrowAt,
|
|
224
|
-
tinyAt: typeof quotaToastConfig.layout?.tinyAt === "number" && quotaToastConfig.layout.tinyAt > 0
|
|
225
|
-
? quotaToastConfig.layout.tinyAt
|
|
226
|
-
: DEFAULT_CONFIG.layout.tinyAt,
|
|
227
|
-
},
|
|
228
|
-
};
|
|
229
|
-
// enabledProviders: "auto" means auto-detect; explicit array means user-specified.
|
|
230
|
-
// Ensure at least one Google model is configured
|
|
231
|
-
if (config.googleModels.length === 0) {
|
|
232
|
-
config.googleModels = DEFAULT_CONFIG.googleModels;
|
|
233
|
-
}
|
|
234
|
-
return config;
|
|
235
|
-
}
|
|
236
477
|
async function readJson(path) {
|
|
237
478
|
try {
|
|
238
479
|
const content = await readFile(path, "utf-8");
|
|
@@ -242,80 +483,58 @@ export async function loadConfig(client, meta, options) {
|
|
|
242
483
|
return null;
|
|
243
484
|
}
|
|
244
485
|
}
|
|
245
|
-
async function loadQuotaToastFromLocations(locations) {
|
|
246
|
-
const quota = {};
|
|
247
|
-
const usedPaths = [];
|
|
248
|
-
const networkSettingSources = {};
|
|
249
|
-
for (const dir of locations) {
|
|
250
|
-
for (const filename of ["opencode.json", "opencode.jsonc"]) {
|
|
251
|
-
const p = join(dir, filename);
|
|
252
|
-
if (!existsSync(p))
|
|
253
|
-
continue;
|
|
254
|
-
const parsed = await readJson(p);
|
|
255
|
-
if (!parsed || typeof parsed !== "object")
|
|
256
|
-
continue;
|
|
257
|
-
const root = parsed;
|
|
258
|
-
const rawQuotaToast = root?.experimental?.quotaToast;
|
|
259
|
-
if (!rawQuotaToast || typeof rawQuotaToast !== "object")
|
|
260
|
-
continue;
|
|
261
|
-
Object.assign(quota, rawQuotaToast);
|
|
262
|
-
const sourcePath = `${p} (experimental.quotaToast)`;
|
|
263
|
-
usedPaths.push(sourcePath);
|
|
264
|
-
recordNetworkSettingSource(networkSettingSources, rawQuotaToast, sourcePath);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return { quota, usedPaths, networkSettingSources };
|
|
268
|
-
}
|
|
269
486
|
async function loadFromFiles() {
|
|
270
|
-
const
|
|
487
|
+
const configRootDir = options?.configRootDir ?? options?.cwd ?? process.cwd();
|
|
271
488
|
const { configDirs } = getOpencodeRuntimeDirCandidates();
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
const quota = {
|
|
283
|
-
...globalConfig.quota,
|
|
284
|
-
...localConfig.quota,
|
|
285
|
-
};
|
|
286
|
-
for (const key of NETWORK_AFFECTING_KEYS) {
|
|
287
|
-
if (hasOwnKey(globalConfig.quota, key)) {
|
|
288
|
-
quota[key] = globalConfig.quota[key];
|
|
489
|
+
const config = cloneDefaultConfig();
|
|
490
|
+
const usedPaths = [];
|
|
491
|
+
const globalConfigPaths = [];
|
|
492
|
+
const workspaceConfigPaths = [];
|
|
493
|
+
const settingSources = {};
|
|
494
|
+
const configIssues = [];
|
|
495
|
+
for (const candidate of buildConfigLayerCandidates(configDirs, configRootDir)) {
|
|
496
|
+
if (!existsSync(candidate.path)) {
|
|
497
|
+
continue;
|
|
289
498
|
}
|
|
290
|
-
|
|
291
|
-
|
|
499
|
+
const parsed = await readJson(candidate.path);
|
|
500
|
+
if (!isPlainObject(parsed) || !isPlainObject(parsed.experimental)) {
|
|
501
|
+
continue;
|
|
292
502
|
}
|
|
503
|
+
const rawQuotaToast = parsed.experimental.quotaToast;
|
|
504
|
+
if (!isPlainObject(rawQuotaToast)) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const sourcePath = `${candidate.path} (experimental.quotaToast)`;
|
|
508
|
+
usedPaths.push(sourcePath);
|
|
509
|
+
if (candidate.scope === "global") {
|
|
510
|
+
globalConfigPaths.push(sourcePath);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
workspaceConfigPaths.push(sourcePath);
|
|
514
|
+
}
|
|
515
|
+
applyValidatedQuotaToastPatch(config, extractValidatedQuotaToastPatch(rawQuotaToast, (key, message) => {
|
|
516
|
+
configIssues.push({ path: sourcePath, key, message });
|
|
517
|
+
}), sourcePath, settingSources);
|
|
293
518
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (hasMergedPricingSnapshot) {
|
|
305
|
-
quota.pricingSnapshot = mergedPricingSnapshot;
|
|
306
|
-
}
|
|
307
|
-
const localFormatStyle = getConfiguredFormatStyle(localConfig.quota);
|
|
308
|
-
const globalFormatStyle = getConfiguredFormatStyle(globalConfig.quota);
|
|
309
|
-
if (localFormatStyle) {
|
|
310
|
-
quota.formatStyle = localFormatStyle;
|
|
311
|
-
}
|
|
312
|
-
else if (globalFormatStyle) {
|
|
313
|
-
quota.formatStyle = globalFormatStyle;
|
|
519
|
+
if (usedPaths.length === 0) {
|
|
520
|
+
return {
|
|
521
|
+
config: null,
|
|
522
|
+
usedPaths: [],
|
|
523
|
+
globalConfigPaths: [],
|
|
524
|
+
workspaceConfigPaths: [],
|
|
525
|
+
settingSources: {},
|
|
526
|
+
networkSettingSources: {},
|
|
527
|
+
configIssues: [],
|
|
528
|
+
};
|
|
314
529
|
}
|
|
315
530
|
return {
|
|
316
|
-
config
|
|
531
|
+
config,
|
|
317
532
|
usedPaths,
|
|
318
|
-
|
|
533
|
+
globalConfigPaths,
|
|
534
|
+
workspaceConfigPaths,
|
|
535
|
+
settingSources,
|
|
536
|
+
networkSettingSources: projectNetworkSettingSources(settingSources),
|
|
537
|
+
configIssues,
|
|
319
538
|
};
|
|
320
539
|
}
|
|
321
540
|
const fileConfig = await loadFromFiles();
|
|
@@ -323,7 +542,11 @@ export async function loadConfig(client, meta, options) {
|
|
|
323
542
|
if (meta) {
|
|
324
543
|
meta.source = "files";
|
|
325
544
|
meta.paths = fileConfig.usedPaths;
|
|
545
|
+
meta.globalConfigPaths = fileConfig.globalConfigPaths;
|
|
546
|
+
meta.workspaceConfigPaths = fileConfig.workspaceConfigPaths;
|
|
547
|
+
meta.settingSources = fileConfig.settingSources;
|
|
326
548
|
meta.networkSettingSources = fileConfig.networkSettingSources;
|
|
549
|
+
meta.configIssues = fileConfig.configIssues;
|
|
327
550
|
}
|
|
328
551
|
return fileConfig.config;
|
|
329
552
|
}
|
|
@@ -333,14 +556,23 @@ export async function loadConfig(client, meta, options) {
|
|
|
333
556
|
// OpenCode config schema is strict; plugin-specific config must live under
|
|
334
557
|
// experimental.* to avoid "unrecognized key" validation errors.
|
|
335
558
|
const quotaToastConfig = response.data?.experimental?.quotaToast;
|
|
336
|
-
if (quotaToastConfig
|
|
559
|
+
if (isPlainObject(quotaToastConfig)) {
|
|
560
|
+
const config = cloneDefaultConfig();
|
|
561
|
+
const settingSources = {};
|
|
562
|
+
const configIssues = [];
|
|
563
|
+
applyValidatedQuotaToastPatch(config, extractValidatedQuotaToastPatch(quotaToastConfig, (key, message) => {
|
|
564
|
+
configIssues.push({ path: "client.config.get", key, message });
|
|
565
|
+
}), "client.config.get", settingSources);
|
|
337
566
|
if (meta) {
|
|
338
567
|
meta.source = "sdk";
|
|
339
568
|
meta.paths = ["client.config.get"];
|
|
340
|
-
meta.
|
|
341
|
-
|
|
569
|
+
meta.globalConfigPaths = [];
|
|
570
|
+
meta.workspaceConfigPaths = [];
|
|
571
|
+
meta.settingSources = settingSources;
|
|
572
|
+
meta.networkSettingSources = projectNetworkSettingSources(settingSources);
|
|
573
|
+
meta.configIssues = configIssues;
|
|
342
574
|
}
|
|
343
|
-
return
|
|
575
|
+
return config;
|
|
344
576
|
}
|
|
345
577
|
}
|
|
346
578
|
catch {
|
|
@@ -350,7 +582,11 @@ export async function loadConfig(client, meta, options) {
|
|
|
350
582
|
if (meta) {
|
|
351
583
|
meta.source = "defaults";
|
|
352
584
|
meta.paths = [];
|
|
585
|
+
meta.globalConfigPaths = [];
|
|
586
|
+
meta.workspaceConfigPaths = [];
|
|
587
|
+
meta.settingSources = {};
|
|
353
588
|
meta.networkSettingSources = {};
|
|
589
|
+
meta.configIssues = [];
|
|
354
590
|
}
|
|
355
591
|
return DEFAULT_CONFIG;
|
|
356
592
|
}
|