opencode-copilot-usage-detector 0.1.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/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +336 -0
- package/dist/aggregator.d.ts +40 -0
- package/dist/aggregator.d.ts.map +1 -0
- package/dist/aggregator.js +254 -0
- package/dist/aggregator.js.map +1 -0
- package/dist/classifier.d.ts +50 -0
- package/dist/classifier.d.ts.map +1 -0
- package/dist/classifier.js +304 -0
- package/dist/classifier.js.map +1 -0
- package/dist/copilot-budget.d.ts +57 -0
- package/dist/copilot-budget.d.ts.map +1 -0
- package/dist/copilot-budget.js +500 -0
- package/dist/copilot-budget.js.map +1 -0
- package/dist/debug.d.ts +7 -0
- package/dist/debug.d.ts.map +1 -0
- package/dist/debug.js +49 -0
- package/dist/debug.js.map +1 -0
- package/dist/estimator.d.ts +92 -0
- package/dist/estimator.d.ts.map +1 -0
- package/dist/estimator.js +523 -0
- package/dist/estimator.js.map +1 -0
- package/dist/github-api.d.ts +11 -0
- package/dist/github-api.d.ts.map +1 -0
- package/dist/github-api.js +273 -0
- package/dist/github-api.js.map +1 -0
- package/dist/persistence.d.ts +28 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +246 -0
- package/dist/persistence.js.map +1 -0
- package/dist/tools.d.ts +20 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +300 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ApiStatus, PremiumRequestSummary, PluginConfig } from "./types.js";
|
|
2
|
+
export declare function parsePremiumRequestResponse(data: unknown, config: PluginConfig): PremiumRequestSummary | null;
|
|
3
|
+
export declare function pollPremiumRequests(config: PluginConfig, force?: boolean): Promise<PremiumRequestSummary | null>;
|
|
4
|
+
export declare function getCachedPremiumRequests(): PremiumRequestSummary | null;
|
|
5
|
+
export declare function getApiStatus(): ApiStatus;
|
|
6
|
+
export declare function needsAuthSetup(): boolean;
|
|
7
|
+
export declare function getAuthSetupMessage(): string | null;
|
|
8
|
+
/** Reset all module-level state. For testing only. */
|
|
9
|
+
export declare function resetApiState(): void;
|
|
10
|
+
export declare function formatPremiumRequestStatus(pr: PremiumRequestSummary): string;
|
|
11
|
+
//# sourceMappingURL=github-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-api.d.ts","sourceRoot":"","sources":["../src/github-api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAEV,SAAS,EACT,qBAAqB,EAErB,YAAY,EACb,MAAM,YAAY,CAAA;AAoKnB,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,YAAY,GACnB,qBAAqB,GAAG,IAAI,CAkE9B;AAMD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,YAAY,EACpB,KAAK,UAAQ,GACZ,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAqBvC;AAED,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,IAAI,CAEvE;AAED,wBAAgB,YAAY,IAAI,SAAS,CAExC;AAED,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,wBAAgB,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAInD;AAED,sDAAsD;AACtD,wBAAgB,aAAa,IAAI,IAAI,CAUpC;AAED,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,qBAAqB,GAAG,MAAM,CAa5E"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
// ============================================================
|
|
6
|
+
// Constants
|
|
7
|
+
// ============================================================
|
|
8
|
+
const POLL_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
|
|
9
|
+
const AUTH_FILE = join(homedir(), ".local", "share", "opencode", "auth.json");
|
|
10
|
+
const GITHUB_API_BASE = "https://api.github.com";
|
|
11
|
+
// Monthly allowances by plan
|
|
12
|
+
const PLAN_ALLOWANCES = {
|
|
13
|
+
free: 50,
|
|
14
|
+
pro: 300,
|
|
15
|
+
"pro+": 1500,
|
|
16
|
+
business: 300,
|
|
17
|
+
enterprise: 1000,
|
|
18
|
+
};
|
|
19
|
+
// ============================================================
|
|
20
|
+
// State
|
|
21
|
+
// ============================================================
|
|
22
|
+
let apiStatus = {
|
|
23
|
+
authMethod: "none",
|
|
24
|
+
username: null,
|
|
25
|
+
lastFetch: 0,
|
|
26
|
+
lastError: null,
|
|
27
|
+
premiumRequests: null,
|
|
28
|
+
};
|
|
29
|
+
let authProbed = false;
|
|
30
|
+
let authNotifiedUser = false;
|
|
31
|
+
// ============================================================
|
|
32
|
+
// Auth probing
|
|
33
|
+
// ============================================================
|
|
34
|
+
function readCopilotToken() {
|
|
35
|
+
try {
|
|
36
|
+
if (!existsSync(AUTH_FILE))
|
|
37
|
+
return null;
|
|
38
|
+
const raw = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
39
|
+
const copilot = raw["github-copilot"];
|
|
40
|
+
if (copilot && typeof copilot.access === "string") {
|
|
41
|
+
return copilot.access;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function tryFetchWithToken(url, token) {
|
|
50
|
+
try {
|
|
51
|
+
const resp = await fetch(url, {
|
|
52
|
+
headers: {
|
|
53
|
+
Accept: "application/vnd.github+json",
|
|
54
|
+
Authorization: `Bearer ${token}`,
|
|
55
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
const data = await resp.json();
|
|
59
|
+
return { ok: resp.ok, status: resp.status, data };
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
return { ok: false, status: 0, data: { error: String(err) } };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function tryGhCli(path) {
|
|
66
|
+
try {
|
|
67
|
+
const result = execFileSync("gh", ["api", path], {
|
|
68
|
+
encoding: "utf-8",
|
|
69
|
+
timeout: 10_000,
|
|
70
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
71
|
+
});
|
|
72
|
+
return { ok: true, data: JSON.parse(result) };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return { ok: false, data: null };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function probeAuth() {
|
|
79
|
+
if (authProbed)
|
|
80
|
+
return;
|
|
81
|
+
authProbed = true;
|
|
82
|
+
// Try 1: Copilot OAuth token
|
|
83
|
+
const copilotToken = readCopilotToken();
|
|
84
|
+
if (copilotToken) {
|
|
85
|
+
const result = await tryFetchWithToken(`${GITHUB_API_BASE}/user`, copilotToken);
|
|
86
|
+
if (result.ok && typeof result.data === "object" && result.data !== null) {
|
|
87
|
+
const login = result.data.login;
|
|
88
|
+
if (typeof login === "string") {
|
|
89
|
+
apiStatus.authMethod = "copilot_token";
|
|
90
|
+
apiStatus.username = login;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Try 2: gh CLI
|
|
96
|
+
const ghResult = tryGhCli("/user");
|
|
97
|
+
if (ghResult.ok && typeof ghResult.data === "object" && ghResult.data !== null) {
|
|
98
|
+
const login = ghResult.data.login;
|
|
99
|
+
if (typeof login === "string") {
|
|
100
|
+
apiStatus.authMethod = "gh_cli";
|
|
101
|
+
apiStatus.username = login;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
apiStatus.authMethod = "none";
|
|
106
|
+
apiStatus.lastError = "No valid auth method found";
|
|
107
|
+
}
|
|
108
|
+
// ============================================================
|
|
109
|
+
// Fetch premium request usage
|
|
110
|
+
// ============================================================
|
|
111
|
+
async function fetchPremiumRequests(config) {
|
|
112
|
+
await probeAuth();
|
|
113
|
+
if (apiStatus.authMethod === "none" || !apiStatus.username) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
const now = new Date();
|
|
117
|
+
const year = now.getUTCFullYear();
|
|
118
|
+
const month = now.getUTCMonth() + 1;
|
|
119
|
+
const path = `/users/${apiStatus.username}/settings/billing/premium_request/usage?year=${year}&month=${month}`;
|
|
120
|
+
let data = null;
|
|
121
|
+
if (apiStatus.authMethod === "copilot_token") {
|
|
122
|
+
const token = readCopilotToken();
|
|
123
|
+
if (!token)
|
|
124
|
+
return null;
|
|
125
|
+
const result = await tryFetchWithToken(`${GITHUB_API_BASE}${path}`, token);
|
|
126
|
+
if (!result.ok) {
|
|
127
|
+
// Token might not have the right scope — try gh CLI as fallback
|
|
128
|
+
apiStatus.authMethod = "gh_cli";
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
data = result.data;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (apiStatus.authMethod === "gh_cli" && !data) {
|
|
135
|
+
const result = tryGhCli(path);
|
|
136
|
+
if (!result.ok) {
|
|
137
|
+
apiStatus.lastError = "gh CLI failed to fetch billing data. Run: gh auth refresh -h github.com -s user";
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
data = result.data;
|
|
141
|
+
}
|
|
142
|
+
if (!data)
|
|
143
|
+
return null;
|
|
144
|
+
// Parse the response
|
|
145
|
+
return parsePremiumRequestResponse(data, config);
|
|
146
|
+
}
|
|
147
|
+
export function parsePremiumRequestResponse(data, config) {
|
|
148
|
+
try {
|
|
149
|
+
// The API may return different structures depending on the endpoint version
|
|
150
|
+
// Handle both array and object responses
|
|
151
|
+
let items = [];
|
|
152
|
+
if (Array.isArray(data)) {
|
|
153
|
+
items = data;
|
|
154
|
+
}
|
|
155
|
+
else if (typeof data === "object" && data !== null) {
|
|
156
|
+
const obj = data;
|
|
157
|
+
if (Array.isArray(obj.usageItems)) {
|
|
158
|
+
items = obj.usageItems;
|
|
159
|
+
}
|
|
160
|
+
else if (Array.isArray(obj.usage_items)) {
|
|
161
|
+
items = obj.usage_items;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (items.length === 0 && typeof data === "object" && data !== null) {
|
|
165
|
+
// Maybe the response itself is the summary
|
|
166
|
+
const obj = data;
|
|
167
|
+
if (typeof obj.total_premium_requests === "number" || typeof obj.totalPremiumRequests === "number") {
|
|
168
|
+
const total = (obj.total_premium_requests ?? obj.totalPremiumRequests);
|
|
169
|
+
const allowance = PLAN_ALLOWANCES[config.copilot_plan.toLowerCase()] ?? config.monthly_premium_allowance;
|
|
170
|
+
return {
|
|
171
|
+
totalPremiumRequests: total,
|
|
172
|
+
byModel: {},
|
|
173
|
+
byProduct: {},
|
|
174
|
+
monthlyAllowance: allowance,
|
|
175
|
+
remaining: Math.max(0, allowance - total),
|
|
176
|
+
percentUsed: allowance > 0 ? Math.round((total / allowance) * 100) : 0,
|
|
177
|
+
fetchedAt: new Date().toISOString(),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Parse individual items
|
|
182
|
+
const byModel = {};
|
|
183
|
+
const byProduct = {};
|
|
184
|
+
let totalQuantity = 0;
|
|
185
|
+
for (const item of items) {
|
|
186
|
+
if (typeof item !== "object" || item === null)
|
|
187
|
+
continue;
|
|
188
|
+
const entry = item;
|
|
189
|
+
const quantity = typeof entry.quantity === "number" ? entry.quantity : 0;
|
|
190
|
+
const sku = typeof entry.sku === "string" ? entry.sku : "unknown";
|
|
191
|
+
const product = typeof entry.product === "string" ? entry.product : "unknown";
|
|
192
|
+
totalQuantity += quantity;
|
|
193
|
+
byModel[sku] = (byModel[sku] ?? 0) + quantity;
|
|
194
|
+
byProduct[product] = (byProduct[product] ?? 0) + quantity;
|
|
195
|
+
}
|
|
196
|
+
const allowance = PLAN_ALLOWANCES[config.copilot_plan.toLowerCase()] ?? config.monthly_premium_allowance;
|
|
197
|
+
return {
|
|
198
|
+
totalPremiumRequests: totalQuantity,
|
|
199
|
+
byModel,
|
|
200
|
+
byProduct,
|
|
201
|
+
monthlyAllowance: allowance,
|
|
202
|
+
remaining: Math.max(0, allowance - totalQuantity),
|
|
203
|
+
percentUsed: allowance > 0 ? Math.round((totalQuantity / allowance) * 100) : 0,
|
|
204
|
+
fetchedAt: new Date().toISOString(),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// ============================================================
|
|
212
|
+
// Public API
|
|
213
|
+
// ============================================================
|
|
214
|
+
export async function pollPremiumRequests(config, force = false) {
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
// Don't poll more often than the interval
|
|
217
|
+
if (!force && now - apiStatus.lastFetch < POLL_INTERVAL_MS) {
|
|
218
|
+
return apiStatus.premiumRequests;
|
|
219
|
+
}
|
|
220
|
+
apiStatus.lastFetch = now;
|
|
221
|
+
try {
|
|
222
|
+
const result = await fetchPremiumRequests(config);
|
|
223
|
+
if (result) {
|
|
224
|
+
apiStatus.premiumRequests = result;
|
|
225
|
+
apiStatus.lastError = null;
|
|
226
|
+
}
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
apiStatus.lastError = err instanceof Error ? err.message : String(err);
|
|
231
|
+
return apiStatus.premiumRequests; // Return cached data on error
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
export function getCachedPremiumRequests() {
|
|
235
|
+
return apiStatus.premiumRequests;
|
|
236
|
+
}
|
|
237
|
+
export function getApiStatus() {
|
|
238
|
+
return { ...apiStatus };
|
|
239
|
+
}
|
|
240
|
+
export function needsAuthSetup() {
|
|
241
|
+
return authProbed && apiStatus.authMethod === "none";
|
|
242
|
+
}
|
|
243
|
+
export function getAuthSetupMessage() {
|
|
244
|
+
if (!needsAuthSetup() || authNotifiedUser)
|
|
245
|
+
return null;
|
|
246
|
+
authNotifiedUser = true;
|
|
247
|
+
return "To enable premium request tracking from the GitHub API, run:\n`gh auth refresh -h github.com -s user`\nThen restart OpenCode. Without this, the plugin uses empirical tracking only.";
|
|
248
|
+
}
|
|
249
|
+
/** Reset all module-level state. For testing only. */
|
|
250
|
+
export function resetApiState() {
|
|
251
|
+
apiStatus = {
|
|
252
|
+
authMethod: "none",
|
|
253
|
+
username: null,
|
|
254
|
+
lastFetch: 0,
|
|
255
|
+
lastError: null,
|
|
256
|
+
premiumRequests: null,
|
|
257
|
+
};
|
|
258
|
+
authProbed = false;
|
|
259
|
+
authNotifiedUser = false;
|
|
260
|
+
}
|
|
261
|
+
export function formatPremiumRequestStatus(pr) {
|
|
262
|
+
const lines = [
|
|
263
|
+
`Premium requests this month: ${pr.totalPremiumRequests} / ${pr.monthlyAllowance} (${pr.percentUsed}% used, ${pr.remaining} remaining)`,
|
|
264
|
+
];
|
|
265
|
+
const modelEntries = Object.entries(pr.byModel);
|
|
266
|
+
if (modelEntries.length > 0) {
|
|
267
|
+
for (const [model, count] of modelEntries.sort((a, b) => b[1] - a[1])) {
|
|
268
|
+
lines.push(` ${model}: ${count} requests`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return lines.join("\n");
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=github-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-api.js","sourceRoot":"","sources":["../src/github-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ObservationEvent, PluginConfig } from "./types.js";
|
|
2
|
+
export declare function ensureDataDir(): void;
|
|
3
|
+
export declare function isWritable(): boolean;
|
|
4
|
+
export declare function appendObservation(event: ObservationEvent): void;
|
|
5
|
+
export declare function readObservations(filter?: {
|
|
6
|
+
since?: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
}): ObservationEvent[];
|
|
9
|
+
export declare function readTodayObservations(today: string): ObservationEvent[];
|
|
10
|
+
export declare function readEstimates(): Record<string, unknown> | null;
|
|
11
|
+
export declare function writeEstimates(data: Record<string, unknown> | object): void;
|
|
12
|
+
export declare function readConfig(): PluginConfig;
|
|
13
|
+
export declare function getDataDir(): string;
|
|
14
|
+
export interface PersistenceInstance {
|
|
15
|
+
ensureDataDir(): void;
|
|
16
|
+
appendObservation(event: ObservationEvent): void;
|
|
17
|
+
readObservations(filter?: {
|
|
18
|
+
since?: string;
|
|
19
|
+
type?: string;
|
|
20
|
+
}): ObservationEvent[];
|
|
21
|
+
readTodayObservations(today: string): ObservationEvent[];
|
|
22
|
+
readEstimates(): Record<string, unknown> | null;
|
|
23
|
+
writeEstimates(data: Record<string, unknown> | object): void;
|
|
24
|
+
readConfig(): PluginConfig;
|
|
25
|
+
getDataDir(): string;
|
|
26
|
+
}
|
|
27
|
+
export declare function createPersistence(dataDir: string): PersistenceInstance;
|
|
28
|
+
//# sourceMappingURL=persistence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAahE,wBAAgB,aAAa,IAAI,IAAI,CASpC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC;AAMD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAY/D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE;IACxC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GAAG,gBAAgB,EAAE,CAerB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAEvE;AAgFD,wBAAgB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAO9D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAQ3E;AAMD,wBAAgB,UAAU,IAAI,YAAY,CAQzC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAMD,MAAM,WAAW,mBAAmB;IAClC,aAAa,IAAI,IAAI,CAAA;IACrB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAChD,gBAAgB,CAAC,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,gBAAgB,EAAE,CAAA;IAChF,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAAA;IACxD,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;IAC/C,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAAA;IAC5D,UAAU,IAAI,YAAY,CAAA;IAC1B,UAAU,IAAI,MAAM,CAAA;CACrB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAkEtE"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { appendFileSync, readFileSync, writeFileSync, renameSync, existsSync, mkdirSync, statSync, } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { DEFAULT_CONFIG } from "./types.js";
|
|
5
|
+
const DATA_DIR = join(homedir(), ".config", "copilot-budget");
|
|
6
|
+
const OBSERVATIONS_FILE = join(DATA_DIR, "observations.jsonl");
|
|
7
|
+
const ESTIMATES_FILE = join(DATA_DIR, "estimates.json");
|
|
8
|
+
const CONFIG_FILE = join(DATA_DIR, "config.json");
|
|
9
|
+
const MAX_JSONL_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
10
|
+
const MAX_JSONL_AGE_DAYS = 90;
|
|
11
|
+
let writable = true; // Track if the data dir is writable
|
|
12
|
+
export function ensureDataDir() {
|
|
13
|
+
try {
|
|
14
|
+
if (!existsSync(DATA_DIR)) {
|
|
15
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
writable = true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
writable = false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function isWritable() {
|
|
24
|
+
return writable;
|
|
25
|
+
}
|
|
26
|
+
// ============================================================
|
|
27
|
+
// observations.jsonl
|
|
28
|
+
// ============================================================
|
|
29
|
+
export function appendObservation(event) {
|
|
30
|
+
if (!writable)
|
|
31
|
+
return;
|
|
32
|
+
try {
|
|
33
|
+
ensureDataDir();
|
|
34
|
+
const line = JSON.stringify(event);
|
|
35
|
+
appendFileSync(OBSERVATIONS_FILE, line + "\n");
|
|
36
|
+
// Check if rotation needed
|
|
37
|
+
maybeRotateJsonl();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Graceful degradation — don't crash if we can't write
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function readObservations(filter) {
|
|
44
|
+
if (!existsSync(OBSERVATIONS_FILE))
|
|
45
|
+
return [];
|
|
46
|
+
try {
|
|
47
|
+
return readFileSync(OBSERVATIONS_FILE, "utf-8")
|
|
48
|
+
.split("\n")
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.map((line) => JSON.parse(line))
|
|
51
|
+
.filter((e) => {
|
|
52
|
+
if (filter?.since && e.ts < filter.since)
|
|
53
|
+
return false;
|
|
54
|
+
if (filter?.type && e.type !== filter.type)
|
|
55
|
+
return false;
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function readTodayObservations(today) {
|
|
64
|
+
return readObservations({ since: today + "T00:00:00" });
|
|
65
|
+
}
|
|
66
|
+
// ============================================================
|
|
67
|
+
// JSONL rotation
|
|
68
|
+
// ============================================================
|
|
69
|
+
let lastRotationCheck = 0;
|
|
70
|
+
function maybeRotateJsonl() {
|
|
71
|
+
// Only check every 5 minutes
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
if (now - lastRotationCheck < 5 * 60 * 1000)
|
|
74
|
+
return;
|
|
75
|
+
lastRotationCheck = now;
|
|
76
|
+
try {
|
|
77
|
+
if (!existsSync(OBSERVATIONS_FILE))
|
|
78
|
+
return;
|
|
79
|
+
const stats = statSync(OBSERVATIONS_FILE);
|
|
80
|
+
// Rotate by size
|
|
81
|
+
if (stats.size > MAX_JSONL_SIZE_BYTES) {
|
|
82
|
+
rotateJsonl("size_exceeded");
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
// Rotate by age: check if the oldest entry is > MAX_JSONL_AGE_DAYS old
|
|
86
|
+
const allObs = readObservations();
|
|
87
|
+
if (allObs.length > 0) {
|
|
88
|
+
const oldest = new Date(allObs[0].ts);
|
|
89
|
+
const ageDays = (Date.now() - oldest.getTime()) / (24 * 60 * 60 * 1000);
|
|
90
|
+
if (ageDays > MAX_JSONL_AGE_DAYS) {
|
|
91
|
+
// Keep last 30 days, archive the rest
|
|
92
|
+
const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
|
|
93
|
+
const recent = allObs.filter((e) => e.ts >= cutoff);
|
|
94
|
+
// Write recent events to temp file, then swap with rollback on failure
|
|
95
|
+
const recentContent = recent.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
96
|
+
const tempPath = OBSERVATIONS_FILE + ".tmp";
|
|
97
|
+
writeFileSync(tempPath, recentContent);
|
|
98
|
+
const archivePath = safeArchivePath("archive");
|
|
99
|
+
renameSync(OBSERVATIONS_FILE, archivePath);
|
|
100
|
+
try {
|
|
101
|
+
renameSync(tempPath, OBSERVATIONS_FILE);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Rollback: restore original from archive
|
|
105
|
+
try {
|
|
106
|
+
renameSync(archivePath, OBSERVATIONS_FILE);
|
|
107
|
+
}
|
|
108
|
+
catch { /* best effort */ }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Rotation failure is not critical
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function safeArchivePath(reason) {
|
|
118
|
+
// Use full ISO timestamp (colons replaced for filesystem safety) to avoid collisions
|
|
119
|
+
const ts = new Date().toISOString().replace(/:/g, "-");
|
|
120
|
+
let path = join(DATA_DIR, `observations.${ts}.${reason}.jsonl`);
|
|
121
|
+
// If somehow exists (very unlikely with millisecond timestamps), add a counter
|
|
122
|
+
let counter = 1;
|
|
123
|
+
while (existsSync(path)) {
|
|
124
|
+
path = join(DATA_DIR, `observations.${ts}.${reason}.${counter}.jsonl`);
|
|
125
|
+
counter++;
|
|
126
|
+
}
|
|
127
|
+
return path;
|
|
128
|
+
}
|
|
129
|
+
function rotateJsonl(reason) {
|
|
130
|
+
try {
|
|
131
|
+
const archivePath = safeArchivePath(reason);
|
|
132
|
+
renameSync(OBSERVATIONS_FILE, archivePath);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Archive failure is not critical
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// ============================================================
|
|
139
|
+
// estimates.json
|
|
140
|
+
// ============================================================
|
|
141
|
+
export function readEstimates() {
|
|
142
|
+
if (!existsSync(ESTIMATES_FILE))
|
|
143
|
+
return null;
|
|
144
|
+
try {
|
|
145
|
+
return JSON.parse(readFileSync(ESTIMATES_FILE, "utf-8"));
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function writeEstimates(data) {
|
|
152
|
+
if (!writable)
|
|
153
|
+
return;
|
|
154
|
+
try {
|
|
155
|
+
ensureDataDir();
|
|
156
|
+
writeFileSync(ESTIMATES_FILE, JSON.stringify(data, null, 2) + "\n");
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Graceful degradation
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// ============================================================
|
|
163
|
+
// config.json
|
|
164
|
+
// ============================================================
|
|
165
|
+
export function readConfig() {
|
|
166
|
+
if (!existsSync(CONFIG_FILE))
|
|
167
|
+
return { ...DEFAULT_CONFIG };
|
|
168
|
+
try {
|
|
169
|
+
const raw = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
170
|
+
return { ...DEFAULT_CONFIG, ...raw };
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return { ...DEFAULT_CONFIG };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
export function getDataDir() {
|
|
177
|
+
return DATA_DIR;
|
|
178
|
+
}
|
|
179
|
+
export function createPersistence(dataDir) {
|
|
180
|
+
const obsFile = join(dataDir, "observations.jsonl");
|
|
181
|
+
const estFile = join(dataDir, "estimates.json");
|
|
182
|
+
const cfgFile = join(dataDir, "config.json");
|
|
183
|
+
return {
|
|
184
|
+
ensureDataDir() {
|
|
185
|
+
if (!existsSync(dataDir)) {
|
|
186
|
+
mkdirSync(dataDir, { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
appendObservation(event) {
|
|
190
|
+
this.ensureDataDir();
|
|
191
|
+
appendFileSync(obsFile, JSON.stringify(event) + "\n");
|
|
192
|
+
},
|
|
193
|
+
readObservations(filter) {
|
|
194
|
+
if (!existsSync(obsFile))
|
|
195
|
+
return [];
|
|
196
|
+
try {
|
|
197
|
+
return readFileSync(obsFile, "utf-8")
|
|
198
|
+
.split("\n")
|
|
199
|
+
.filter(Boolean)
|
|
200
|
+
.map((line) => JSON.parse(line))
|
|
201
|
+
.filter((e) => {
|
|
202
|
+
if (filter?.since && e.ts < filter.since)
|
|
203
|
+
return false;
|
|
204
|
+
if (filter?.type && e.type !== filter.type)
|
|
205
|
+
return false;
|
|
206
|
+
return true;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
readTodayObservations(today) {
|
|
214
|
+
return this.readObservations({ since: today + "T00:00:00" });
|
|
215
|
+
},
|
|
216
|
+
readEstimates() {
|
|
217
|
+
if (!existsSync(estFile))
|
|
218
|
+
return null;
|
|
219
|
+
try {
|
|
220
|
+
return JSON.parse(readFileSync(estFile, "utf-8"));
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
writeEstimates(data) {
|
|
227
|
+
this.ensureDataDir();
|
|
228
|
+
writeFileSync(estFile, JSON.stringify(data, null, 2) + "\n");
|
|
229
|
+
},
|
|
230
|
+
readConfig() {
|
|
231
|
+
if (!existsSync(cfgFile))
|
|
232
|
+
return { ...DEFAULT_CONFIG };
|
|
233
|
+
try {
|
|
234
|
+
const raw = JSON.parse(readFileSync(cfgFile, "utf-8"));
|
|
235
|
+
return { ...DEFAULT_CONFIG, ...raw };
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return { ...DEFAULT_CONFIG };
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
getDataDir() {
|
|
242
|
+
return dataDir;
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function formatStatus(): string;
|
|
2
|
+
export declare function formatHistory(days: number): string;
|
|
3
|
+
export declare function formatErrors(): string;
|
|
4
|
+
export declare function formatInsights(): string;
|
|
5
|
+
export declare const budgetTool: {
|
|
6
|
+
description: string;
|
|
7
|
+
args: {
|
|
8
|
+
action: import("zod").ZodEnum<{
|
|
9
|
+
insights: "insights";
|
|
10
|
+
status: "status";
|
|
11
|
+
history: "history";
|
|
12
|
+
errors: "errors";
|
|
13
|
+
recompute: "recompute";
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
execute(args: {
|
|
17
|
+
action: "insights" | "status" | "history" | "errors" | "recompute";
|
|
18
|
+
}, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAYA,wBAAgB,YAAY,IAAI,MAAM,CA8HrC;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwBlD;AAED,wBAAgB,YAAY,IAAI,MAAM,CA0ErC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAoFvC;AAED,eAAO,MAAM,UAAU;;;;;;;;;;;;;;CA6BrB,CAAA"}
|