pi-harness-runtime 1.1.81 → 1.1.83
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/harness/browser-scrapers/base-browser-scraper.d.ts +91 -0
- package/harness/browser-scrapers/chrome-profile-manager.d.ts +80 -0
- package/harness/browser-scrapers/codex-browser-scraper.d.ts +60 -0
- package/harness/browser-scrapers/index.d.ts +29 -0
- package/harness/browser-scrapers/zai-browser-scraper.d.ts +79 -0
- package/harness/e2e/browser-scrapers/base-browser-scraper.d.ts +92 -0
- package/harness/e2e/browser-scrapers/base-browser-scraper.ts +244 -0
- package/harness/e2e/browser-scrapers/chrome-profile-manager.d.ts +81 -0
- package/harness/e2e/browser-scrapers/chrome-profile-manager.ts +297 -0
- package/harness/e2e/browser-scrapers/codex-browser-scraper.d.ts +61 -0
- package/harness/e2e/browser-scrapers/codex-browser-scraper.ts +343 -0
- package/harness/e2e/browser-scrapers/index.d.ts +30 -0
- package/harness/e2e/browser-scrapers/index.ts +48 -0
- package/harness/e2e/browser-scrapers/zai-browser-scraper.d.ts +80 -0
- package/harness/e2e/browser-scrapers/zai-browser-scraper.ts +392 -0
- package/harness/e2e/glm-quota-scraper.ts +106 -15
- package/harness/e2e/index.ts +29 -0
- package/harness/e2e/unified-quota-manager.ts +364 -0
- package/index.ts +90 -1
- package/mirror.ts +32 -28
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/dist/provider-id.d.ts.map +1 -1
- package/packages/providers/dist/provider-id.js +1 -2
- package/packages/providers/dist/provider-id.js.map +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/providers/src/provider-id.ts +1 -2
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Quota Manager — Handles all providers with appropriate auth
|
|
3
|
+
*
|
|
4
|
+
* Authentication strategies by provider:
|
|
5
|
+
* | Provider | Auth Method | Status |
|
|
6
|
+
* |------------|----------------|-----------|
|
|
7
|
+
* | MiniMax | Browser cookies| ✅ Working|
|
|
8
|
+
* | GLM/z.ai | API Key | ✅ Exists |
|
|
9
|
+
* | ChatGPT | OAuth tokens | ✅ Exists |
|
|
10
|
+
* | Codex | OAuth tokens | ✅ Exists |
|
|
11
|
+
* | OpenAI | API Key | ✅ Exists |
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
MiniMaxQuotaManager,
|
|
16
|
+
type MiniMaxQuotaData,
|
|
17
|
+
} from "./minimax-quota-scraper.js";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
GLMQuotaManager,
|
|
21
|
+
GLMQuotaScraper,
|
|
22
|
+
type GLMQuotaData,
|
|
23
|
+
} from "./glm-quota-scraper.js";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
ChatGPTQuotaManager,
|
|
27
|
+
ChatGPTQuotaScraper,
|
|
28
|
+
type ChatGPTQuotaData,
|
|
29
|
+
} from "./chatgpt-quota-scraper.js";
|
|
30
|
+
|
|
31
|
+
import {
|
|
32
|
+
OpenAIQuotaManager,
|
|
33
|
+
OpenAIQuotaScraper,
|
|
34
|
+
type OpenAIQuotaData,
|
|
35
|
+
} from "./openai-quota-scraper.js";
|
|
36
|
+
|
|
37
|
+
export type Provider = "minimax" | "glm" | "chatgpt" | "openai-codex" | "openai";
|
|
38
|
+
|
|
39
|
+
export interface QuotaData {
|
|
40
|
+
provider: Provider;
|
|
41
|
+
/** Usage percentage (0-100) */
|
|
42
|
+
usagePercent: number;
|
|
43
|
+
/** Human-readable reset time */
|
|
44
|
+
resetsAt?: string;
|
|
45
|
+
/** Epoch ms when quota resets */
|
|
46
|
+
resetsAtEpoch?: number;
|
|
47
|
+
/** Plan type */
|
|
48
|
+
planType?: string;
|
|
49
|
+
/** Total quota amount */
|
|
50
|
+
totalQuota?: string;
|
|
51
|
+
/** Used quota amount */
|
|
52
|
+
usedQuota?: string;
|
|
53
|
+
/** Remaining quota */
|
|
54
|
+
remainingQuota?: string;
|
|
55
|
+
/** Raw data */
|
|
56
|
+
raw?: Record<string, unknown>;
|
|
57
|
+
/** Timestamp */
|
|
58
|
+
scrapedAt: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface UnifiedQuotaConfig {
|
|
62
|
+
/** Provider to use (auto-detect if not specified) */
|
|
63
|
+
provider?: Provider;
|
|
64
|
+
/** Cookie file path (for browser-based providers) */
|
|
65
|
+
cookieFile?: string;
|
|
66
|
+
/** API key file path (for API-based providers) */
|
|
67
|
+
apiKeyFile?: string;
|
|
68
|
+
/** Direct API key value */
|
|
69
|
+
apiKey?: string;
|
|
70
|
+
/** Auth file path (for OAuth-based providers) */
|
|
71
|
+
authFile?: string;
|
|
72
|
+
/** Cache duration in ms (default: 5 min) */
|
|
73
|
+
cacheDurationMs?: number;
|
|
74
|
+
/** Suppress console output */
|
|
75
|
+
quiet?: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface QuotaManager {
|
|
79
|
+
isAvailable(): boolean;
|
|
80
|
+
getQuota(forceRefresh?: boolean): Promise<unknown>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Unified Quota Manager
|
|
85
|
+
*
|
|
86
|
+
* Provides a single interface to query quota data from all supported providers.
|
|
87
|
+
* Automatically detects available providers based on credentials.
|
|
88
|
+
*/
|
|
89
|
+
export class UnifiedQuotaManager {
|
|
90
|
+
private managers: Map<Provider, QuotaManager> = new Map();
|
|
91
|
+
private activeProvider?: Provider;
|
|
92
|
+
private cacheDurationMs: number;
|
|
93
|
+
private quiet: boolean;
|
|
94
|
+
|
|
95
|
+
constructor(config: UnifiedQuotaConfig = {}) {
|
|
96
|
+
this.cacheDurationMs = config.cacheDurationMs ?? 5 * 60 * 1000;
|
|
97
|
+
this.quiet = config.quiet ?? false;
|
|
98
|
+
|
|
99
|
+
// Initialize managers based on config
|
|
100
|
+
if (!config.provider || config.provider === "minimax") {
|
|
101
|
+
const mmManager = new MiniMaxQuotaManager({
|
|
102
|
+
cookieFile: config.cookieFile,
|
|
103
|
+
cacheDurationMs: this.cacheDurationMs,
|
|
104
|
+
quiet: this.quiet,
|
|
105
|
+
});
|
|
106
|
+
this.managers.set("minimax", mmManager);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!config.provider || config.provider === "glm") {
|
|
110
|
+
const glmManager = new GLMQuotaManager({
|
|
111
|
+
apiKey: config.apiKey,
|
|
112
|
+
apiKeyFile: config.apiKeyFile,
|
|
113
|
+
quiet: this.quiet,
|
|
114
|
+
});
|
|
115
|
+
this.managers.set("glm", glmManager);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!config.provider || config.provider === "chatgpt" || config.provider === "openai-codex") {
|
|
119
|
+
const chatgptManager = new ChatGPTQuotaManager({
|
|
120
|
+
authFile: config.authFile,
|
|
121
|
+
quiet: this.quiet,
|
|
122
|
+
});
|
|
123
|
+
this.managers.set("chatgpt", chatgptManager);
|
|
124
|
+
// openai-codex uses the same ChatGPT quota endpoint
|
|
125
|
+
this.managers.set("openai-codex", chatgptManager);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!config.provider || config.provider === "openai") {
|
|
129
|
+
const openaiManager = new OpenAIQuotaManager({
|
|
130
|
+
cookieFile: config.cookieFile,
|
|
131
|
+
quiet: this.quiet,
|
|
132
|
+
});
|
|
133
|
+
this.managers.set("openai", openaiManager);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Set active provider
|
|
137
|
+
if (config.provider) {
|
|
138
|
+
this.activeProvider = config.provider;
|
|
139
|
+
} else {
|
|
140
|
+
// Auto-detect first available provider
|
|
141
|
+
this.activeProvider = this.detectAvailableProvider();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Detect the first available provider
|
|
147
|
+
*/
|
|
148
|
+
private detectAvailableProvider(): Provider | undefined {
|
|
149
|
+
const priority: Provider[] = ["minimax", "glm", "chatgpt", "openai-codex", "openai"];
|
|
150
|
+
|
|
151
|
+
for (const provider of priority) {
|
|
152
|
+
const manager = this.managers.get(provider);
|
|
153
|
+
if (manager && manager.isAvailable()) {
|
|
154
|
+
if (!this.quiet) {
|
|
155
|
+
console.log(`[UnifiedQuota] Auto-detected provider: ${provider}`);
|
|
156
|
+
}
|
|
157
|
+
return provider;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get all available providers
|
|
166
|
+
*/
|
|
167
|
+
getAvailableProviders(): Provider[] {
|
|
168
|
+
const available: Provider[] = [];
|
|
169
|
+
for (const [provider, manager] of this.managers) {
|
|
170
|
+
if (manager.isAvailable()) {
|
|
171
|
+
available.push(provider);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return available;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Check if a provider is available
|
|
179
|
+
*/
|
|
180
|
+
isProviderAvailable(provider: Provider): boolean {
|
|
181
|
+
const manager = this.managers.get(provider);
|
|
182
|
+
return manager?.isAvailable() ?? false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Set active provider
|
|
187
|
+
*/
|
|
188
|
+
setProvider(provider: Provider): boolean {
|
|
189
|
+
const manager = this.managers.get(provider);
|
|
190
|
+
if (manager && manager.isAvailable()) {
|
|
191
|
+
this.activeProvider = provider;
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get current provider
|
|
199
|
+
*/
|
|
200
|
+
getProvider(): Provider | undefined {
|
|
201
|
+
return this.activeProvider;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get quota data from active provider
|
|
206
|
+
*/
|
|
207
|
+
async getQuota(forceRefresh = false): Promise<QuotaData | null> {
|
|
208
|
+
if (!this.activeProvider) {
|
|
209
|
+
if (!this.quiet) {
|
|
210
|
+
console.warn("[UnifiedQuota] No provider available");
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const manager = this.managers.get(this.activeProvider);
|
|
216
|
+
if (!manager) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const data = await manager.getQuota(forceRefresh);
|
|
222
|
+
if (!data) return null;
|
|
223
|
+
|
|
224
|
+
return this.normalizeData(this.activeProvider, data);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
if (!this.quiet) {
|
|
227
|
+
console.error(`[UnifiedQuota] Failed to fetch quota:`, error);
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Get quota from specific provider
|
|
235
|
+
*/
|
|
236
|
+
async getQuotaForProvider(
|
|
237
|
+
provider: Provider,
|
|
238
|
+
forceRefresh = false,
|
|
239
|
+
): Promise<QuotaData | null> {
|
|
240
|
+
const manager = this.managers.get(provider);
|
|
241
|
+
if (!manager) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const data = await manager.getQuota(forceRefresh);
|
|
247
|
+
if (!data) return null;
|
|
248
|
+
|
|
249
|
+
return this.normalizeData(provider, data);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
if (!this.quiet) {
|
|
252
|
+
console.error(`[UnifiedQuota] Failed to fetch ${provider} quota:`, error);
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Normalize provider-specific data to unified format
|
|
260
|
+
*/
|
|
261
|
+
private normalizeData(
|
|
262
|
+
provider: Provider,
|
|
263
|
+
data: unknown,
|
|
264
|
+
): QuotaData {
|
|
265
|
+
const base: QuotaData = {
|
|
266
|
+
provider,
|
|
267
|
+
usagePercent: 0,
|
|
268
|
+
scrapedAt: new Date().toISOString(),
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
switch (provider) {
|
|
272
|
+
case "minimax": {
|
|
273
|
+
const d = data as MiniMaxQuotaData;
|
|
274
|
+
return {
|
|
275
|
+
...base,
|
|
276
|
+
usagePercent: d.h5UsedPct,
|
|
277
|
+
resetsAt: d.h5ResetsAt,
|
|
278
|
+
resetsAtEpoch: d.h5ResetsAtEpoch,
|
|
279
|
+
remainingQuota: d.creditBalance,
|
|
280
|
+
// @ts-expect-error - raw access for diagnostics
|
|
281
|
+
raw: d,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
case "glm": {
|
|
286
|
+
const d = data as GLMQuotaData;
|
|
287
|
+
// GLM doesn't provide usage percentage or quota limit directly
|
|
288
|
+
// Show actual usage - percentage is unknown without quota info
|
|
289
|
+
return {
|
|
290
|
+
...base,
|
|
291
|
+
usagePercent: 0, // Unknown - no quota limit in API response
|
|
292
|
+
planType: d.modelName,
|
|
293
|
+
usedQuota: `${(d.totalTokens / 1_000_000).toFixed(1)}M tokens`,
|
|
294
|
+
totalQuota: "unknown",
|
|
295
|
+
// @ts-expect-error - raw access for diagnostics
|
|
296
|
+
raw: d,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
case "chatgpt":
|
|
301
|
+
case "openai-codex": {
|
|
302
|
+
const d = data as ChatGPTQuotaData;
|
|
303
|
+
return {
|
|
304
|
+
...base,
|
|
305
|
+
usagePercent: d.weeklyUsedPct,
|
|
306
|
+
resetsAt: d.weeklyResetsAt,
|
|
307
|
+
resetsAtEpoch: d.weeklyResetsAtEpoch,
|
|
308
|
+
planType: d.planType,
|
|
309
|
+
remainingQuota: d.creditBalance,
|
|
310
|
+
// @ts-expect-error - raw access for diagnostics
|
|
311
|
+
raw: d,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
case "openai": {
|
|
316
|
+
const d = data as OpenAIQuotaData;
|
|
317
|
+
return {
|
|
318
|
+
...base,
|
|
319
|
+
usagePercent: d.weeklyUsedPct,
|
|
320
|
+
resetsAt: d.weeklyResetsAt,
|
|
321
|
+
resetsAtEpoch: d.weeklyResetsAtEpoch,
|
|
322
|
+
remainingQuota: d.creditBalance,
|
|
323
|
+
// @ts-expect-error - raw access for diagnostics
|
|
324
|
+
raw: d,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
default:
|
|
329
|
+
return base;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get summary for status display
|
|
335
|
+
*/
|
|
336
|
+
async getSummary(): Promise<string> {
|
|
337
|
+
const quota = await this.getQuota();
|
|
338
|
+
if (!quota) {
|
|
339
|
+
return `${this.activeProvider ?? "unknown"}: no data`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const left = Math.max(0, 100 - quota.usagePercent);
|
|
343
|
+
const reset = quota.resetsAt ? ` · resets ${quota.resetsAt}` : "";
|
|
344
|
+
|
|
345
|
+
return `${this.activeProvider}: ${left.toFixed(0)}% left${reset}`;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Create unified quota manager from environment variables
|
|
351
|
+
*/
|
|
352
|
+
export function createUnifiedQuotaManagerFromEnv(): UnifiedQuotaManager | null {
|
|
353
|
+
const provider = (process.env.QUOTA_PROVIDER ?? "auto") as Provider | "auto";
|
|
354
|
+
|
|
355
|
+
return new UnifiedQuotaManager({
|
|
356
|
+
provider: provider === "auto" ? undefined : provider,
|
|
357
|
+
cookieFile: process.env.QUOTA_COOKIE_FILE,
|
|
358
|
+
apiKeyFile: process.env.QUOTA_API_KEY_FILE,
|
|
359
|
+
apiKey: process.env.QUOTA_API_KEY,
|
|
360
|
+
authFile: process.env.QUOTA_AUTH_FILE,
|
|
361
|
+
cacheDurationMs: parseInt(process.env.QUOTA_CACHE_MS ?? "300000", 10),
|
|
362
|
+
quiet: process.env.QUOTA_QUIET === "true",
|
|
363
|
+
});
|
|
364
|
+
}
|
package/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { UsageTracker } from "./tracker.ts";
|
|
|
23
23
|
import { MirrorStore, type MirrorRecord } from "./mirror.ts";
|
|
24
24
|
import { MiniMaxQuotaScraper } from "./harness/e2e/minimax-quota-scraper.js";
|
|
25
25
|
import { OpenAIQuotaScraper } from "./harness/e2e/openai-quota-scraper.js";
|
|
26
|
+
import { GLMQuotaScraper } from "./harness/e2e/glm-quota-scraper.js";
|
|
26
27
|
import { parseMiniMaxQuotaText } from "./harness/e2e/minimax-quota-parser.js";
|
|
27
28
|
import {
|
|
28
29
|
CookieWatcher,
|
|
@@ -755,7 +756,9 @@ Run \`bd ready\` to see current bd issues.
|
|
|
755
756
|
// --- Smart quota fetch for OpenAI status -------------------------
|
|
756
757
|
const OPENAI_REFRESH_MIN_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
757
758
|
const _openaiQuotaScraper = new OpenAIQuotaScraper({ quiet: true });
|
|
759
|
+
const _glmQuotaScraper = new GLMQuotaScraper({ quiet: true });
|
|
758
760
|
let lastOpenAIQuotaFetchAt = 0;
|
|
761
|
+
let lastGLMQuotaFetchAt = 0;
|
|
759
762
|
|
|
760
763
|
// --- Cookie sanitizer integration ------------------------------------
|
|
761
764
|
// The drop folder is the user-facing, forgiving input. The canonical
|
|
@@ -1135,6 +1138,47 @@ Run \`bd ready\` to see current bd issues.
|
|
|
1135
1138
|
}
|
|
1136
1139
|
}
|
|
1137
1140
|
|
|
1141
|
+
/**
|
|
1142
|
+
* Auto-fetch GLM quota via z.ai API.
|
|
1143
|
+
* GLM has both 5h and weekly windows.
|
|
1144
|
+
*/
|
|
1145
|
+
async function autoFetchGLMQuota(options?: {
|
|
1146
|
+
suppressErrors?: boolean;
|
|
1147
|
+
}): Promise<boolean> {
|
|
1148
|
+
const suppressErrors = options?.suppressErrors === true;
|
|
1149
|
+
|
|
1150
|
+
try {
|
|
1151
|
+
const data = await _glmQuotaScraper.fetchUsage();
|
|
1152
|
+
if (!data) {
|
|
1153
|
+
if (!suppressErrors) {
|
|
1154
|
+
console.error("[pi-harness] GLM quota auto-fetch: no data returned");
|
|
1155
|
+
}
|
|
1156
|
+
return false;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
writeMirrorRecord("glm", {
|
|
1160
|
+
synced_at: data.scrapedAt,
|
|
1161
|
+
source: "scrape",
|
|
1162
|
+
h5_used_pct: data.h5UsedPct,
|
|
1163
|
+
h5_resets_at: data.h5ResetsAt,
|
|
1164
|
+
h5_resets_at_epoch: data.h5ResetsAtEpoch,
|
|
1165
|
+
weekly_used_pct: data.weeklyUsedPct,
|
|
1166
|
+
weekly_resets_at: data.weeklyResetsAt,
|
|
1167
|
+
weekly_resets_at_epoch: data.weeklyResetsAtEpoch,
|
|
1168
|
+
model: data.modelName,
|
|
1169
|
+
});
|
|
1170
|
+
return true;
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
if (!suppressErrors) {
|
|
1173
|
+
console.error(
|
|
1174
|
+
"[pi-harness] GLM quota auto-fetch skipped:",
|
|
1175
|
+
error instanceof Error ? error.message : String(error),
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1138
1182
|
async function maybeAutoFetchQuota(
|
|
1139
1183
|
modelId: string | null | undefined,
|
|
1140
1184
|
): Promise<void> {
|
|
@@ -1199,7 +1243,52 @@ Run \`bd ready\` to see current bd issues.
|
|
|
1199
1243
|
return;
|
|
1200
1244
|
}
|
|
1201
1245
|
|
|
1202
|
-
//
|
|
1246
|
+
// OpenAI Codex path (uses OAuth tokens from ~/.codex/auth.json)
|
|
1247
|
+
if (provider === "openai-codex") {
|
|
1248
|
+
if (quotaAutoFetchInFlight) return;
|
|
1249
|
+
|
|
1250
|
+
|
|
1251
|
+
const nowMs = Date.now();
|
|
1252
|
+
if (nowMs - lastOpenAIQuotaFetchAt < OPENAI_REFRESH_MIN_INTERVAL_MS) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// Check if Codex auth file exists
|
|
1257
|
+
const codexAuthFile = join(homedir(), ".codex", "auth.json");
|
|
1258
|
+
if (!existsSync(codexAuthFile)) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
quotaAutoFetchInFlight = true;
|
|
1263
|
+
lastOpenAIQuotaFetchAt = nowMs;
|
|
1264
|
+
try {
|
|
1265
|
+
await autoFetchOpenAIQuota({ suppressErrors: true });
|
|
1266
|
+
} finally {
|
|
1267
|
+
quotaAutoFetchInFlight = false;
|
|
1268
|
+
}
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
// GLM path (z.ai has both 5h and weekly windows)
|
|
1273
|
+
if (provider === "glm") {
|
|
1274
|
+
if (quotaAutoFetchInFlight) return;
|
|
1275
|
+
|
|
1276
|
+
const nowMs = Date.now();
|
|
1277
|
+
if (nowMs - lastGLMQuotaFetchAt < OPENAI_REFRESH_MIN_INTERVAL_MS) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
quotaAutoFetchInFlight = true;
|
|
1282
|
+
lastGLMQuotaFetchAt = nowMs;
|
|
1283
|
+
try {
|
|
1284
|
+
await autoFetchGLMQuota({ suppressErrors: true });
|
|
1285
|
+
} finally {
|
|
1286
|
+
quotaAutoFetchInFlight = false;
|
|
1287
|
+
}
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Other providers (Anthropic, OpenRouter) - TUI signal path only for now
|
|
1203
1292
|
return;
|
|
1204
1293
|
}
|
|
1205
1294
|
|
package/mirror.ts
CHANGED
|
@@ -58,34 +58,38 @@ export type MirrorSource = "scrape" | "tui-signal" | "manual";
|
|
|
58
58
|
|
|
59
59
|
/** Per-provider mirror record. */
|
|
60
60
|
export interface ProviderMirrorRecord {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
61
|
+
/** ISO-8601 UTC timestamp when this row was last updated. */
|
|
62
|
+
synced_at: string;
|
|
63
|
+
/** Provider id (e.g. "minimax", "openai", "glm"). Must match the key. */
|
|
64
|
+
provider: string;
|
|
65
|
+
/** Source of the data — used for diagnostics. */
|
|
66
|
+
source: MirrorSource;
|
|
67
|
+
/** Optional model id the data was captured for. */
|
|
68
|
+
model?: string;
|
|
69
|
+
/** 5h window used percent (0-100). Provider-reported when available. */
|
|
70
|
+
h5_used_pct?: number;
|
|
71
|
+
/** When the 5h window resets — human-readable or ISO. */
|
|
72
|
+
h5_resets_at?: string;
|
|
73
|
+
/** Epoch ms when the 5h window resets — precise UTC timestamp. */
|
|
74
|
+
h5_resets_at_epoch?: number;
|
|
75
|
+
/** Weekly used percent. */
|
|
76
|
+
weekly_used_pct?: number;
|
|
77
|
+
/** When the weekly window resets. */
|
|
78
|
+
weekly_resets_at?: string;
|
|
79
|
+
/** Epoch ms when the weekly window resets — precise UTC timestamp. */
|
|
80
|
+
weekly_resets_at_epoch?: number;
|
|
81
|
+
/** Raw tokens used (for GLM which has no percentage). */
|
|
82
|
+
weekly_used_tokens?: number;
|
|
83
|
+
/** Monthly quota in tokens (for GLM). */
|
|
84
|
+
monthly_quota_tokens?: number;
|
|
85
|
+
/** Set when a limit was hit and we have the exhaustion signal. */
|
|
86
|
+
exhausted?: boolean;
|
|
87
|
+
/** Limit type from TUI signal: tokens, context_window, rate_limit, unknown. */
|
|
88
|
+
limitType?: "tokens" | "context_window" | "rate_limit" | "unknown";
|
|
89
|
+
/** Remaining pct (0-100) at exhaustion — 0 means exactly exhausted. */
|
|
90
|
+
remainingPct?: number;
|
|
91
|
+
/** ISO-8601 reset timestamp from TUI signal, if absolute. */
|
|
92
|
+
resets_at?: string;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
/** Map of provider id → its mirror record. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/capability-registry",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.83",
|
|
4
4
|
"description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|