pi-harness-runtime 1.1.80 → 1.1.82
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 +72 -11
- 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,
|
|
@@ -622,7 +623,8 @@ Before running a build, ensure you update the current task status:
|
|
|
622
623
|
Run \`bd ready\` to see current bd issues.
|
|
623
624
|
`;
|
|
624
625
|
|
|
625
|
-
// Detect build commands and
|
|
626
|
+
// Detect build commands and log reminder (don't modify result.content)
|
|
627
|
+
// Modifying result.content is fragile - pi-coding-agent expects specific structures
|
|
626
628
|
pi.on("tool_execution_end", async (event) => {
|
|
627
629
|
const toolName = (event as { toolName?: string }).toolName;
|
|
628
630
|
if (toolName !== "bash") return;
|
|
@@ -630,7 +632,7 @@ Run \`bd ready\` to see current bd issues.
|
|
|
630
632
|
const result = (event as { result?: { content?: unknown } }).result;
|
|
631
633
|
if (!result) return;
|
|
632
634
|
|
|
633
|
-
//
|
|
635
|
+
// Get content for detection only - don't modify result.content
|
|
634
636
|
const rawContent = result.content;
|
|
635
637
|
let content: string;
|
|
636
638
|
if (typeof rawContent === "string") {
|
|
@@ -639,8 +641,10 @@ Run \`bd ready\` to see current bd issues.
|
|
|
639
641
|
content = rawContent
|
|
640
642
|
.map((c) => (typeof c === "string" ? c : JSON.stringify(c)))
|
|
641
643
|
.join("\n");
|
|
644
|
+
} else if (rawContent) {
|
|
645
|
+
content = JSON.stringify(rawContent);
|
|
642
646
|
} else {
|
|
643
|
-
|
|
647
|
+
return; // No content to analyze
|
|
644
648
|
}
|
|
645
649
|
|
|
646
650
|
// Check if this is a build command
|
|
@@ -648,18 +652,13 @@ Run \`bd ready\` to see current bd issues.
|
|
|
648
652
|
content.toLowerCase().includes(cmd.toLowerCase()),
|
|
649
653
|
);
|
|
650
654
|
|
|
655
|
+
// Log reminder to console (agent will see it)
|
|
651
656
|
if (
|
|
652
657
|
isBuildCommand &&
|
|
653
658
|
!content.includes("bd ready") &&
|
|
654
659
|
!content.includes("TODO UPDATE")
|
|
655
660
|
) {
|
|
656
|
-
|
|
657
|
-
const reminderBlock = { type: "text", text: TODO_BUILD_REMINDER };
|
|
658
|
-
if (Array.isArray(rawContent)) {
|
|
659
|
-
result.content = [...rawContent, reminderBlock];
|
|
660
|
-
} else {
|
|
661
|
-
result.content = [{ type: "text", text: content + TODO_BUILD_REMINDER }];
|
|
662
|
-
}
|
|
661
|
+
console.log(TODO_BUILD_REMINDER.trim());
|
|
663
662
|
}
|
|
664
663
|
});
|
|
665
664
|
|
|
@@ -757,7 +756,9 @@ Run \`bd ready\` to see current bd issues.
|
|
|
757
756
|
// --- Smart quota fetch for OpenAI status -------------------------
|
|
758
757
|
const OPENAI_REFRESH_MIN_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
|
|
759
758
|
const _openaiQuotaScraper = new OpenAIQuotaScraper({ quiet: true });
|
|
759
|
+
const _glmQuotaScraper = new GLMQuotaScraper({ quiet: true });
|
|
760
760
|
let lastOpenAIQuotaFetchAt = 0;
|
|
761
|
+
let lastGLMQuotaFetchAt = 0;
|
|
761
762
|
|
|
762
763
|
// --- Cookie sanitizer integration ------------------------------------
|
|
763
764
|
// The drop folder is the user-facing, forgiving input. The canonical
|
|
@@ -1137,6 +1138,47 @@ Run \`bd ready\` to see current bd issues.
|
|
|
1137
1138
|
}
|
|
1138
1139
|
}
|
|
1139
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
|
+
|
|
1140
1182
|
async function maybeAutoFetchQuota(
|
|
1141
1183
|
modelId: string | null | undefined,
|
|
1142
1184
|
): Promise<void> {
|
|
@@ -1201,7 +1243,26 @@ Run \`bd ready\` to see current bd issues.
|
|
|
1201
1243
|
return;
|
|
1202
1244
|
}
|
|
1203
1245
|
|
|
1204
|
-
//
|
|
1246
|
+
// GLM path (z.ai has both 5h and weekly windows)
|
|
1247
|
+
if (provider === "glm") {
|
|
1248
|
+
if (quotaAutoFetchInFlight) return;
|
|
1249
|
+
|
|
1250
|
+
const nowMs = Date.now();
|
|
1251
|
+
if (nowMs - lastGLMQuotaFetchAt < OPENAI_REFRESH_MIN_INTERVAL_MS) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
quotaAutoFetchInFlight = true;
|
|
1256
|
+
lastGLMQuotaFetchAt = nowMs;
|
|
1257
|
+
try {
|
|
1258
|
+
await autoFetchGLMQuota({ suppressErrors: true });
|
|
1259
|
+
} finally {
|
|
1260
|
+
quotaAutoFetchInFlight = false;
|
|
1261
|
+
}
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// Other providers (Anthropic, OpenRouter) - TUI signal path only for now
|
|
1205
1266
|
return;
|
|
1206
1267
|
}
|
|
1207
1268
|
|
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.82",
|
|
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",
|