@getpaseo/server 0.1.97 → 0.1.98

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.
Files changed (69) hide show
  1. package/dist/server/server/agent/agent-manager.d.ts +11 -3
  2. package/dist/server/server/agent/agent-manager.js +94 -22
  3. package/dist/server/server/agent/agent-prompt.d.ts +1 -1
  4. package/dist/server/server/agent/agent-prompt.js +3 -10
  5. package/dist/server/server/agent/agent-sdk-types.d.ts +9 -3
  6. package/dist/server/server/agent/create-agent/create.d.ts +2 -0
  7. package/dist/server/server/agent/create-agent/create.js +8 -7
  8. package/dist/server/server/agent/lifecycle-command.d.ts +15 -1
  9. package/dist/server/server/agent/lifecycle-command.js +9 -2
  10. package/dist/server/server/agent/mcp-server.js +254 -115
  11. package/dist/server/server/agent/provider-notices.d.ts +3 -0
  12. package/dist/server/server/agent/provider-notices.js +5 -0
  13. package/dist/server/server/agent/provider-registry.d.ts +2 -0
  14. package/dist/server/server/agent/provider-registry.js +10 -3
  15. package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -0
  16. package/dist/server/server/agent/provider-snapshot-manager.js +11 -2
  17. package/dist/server/server/agent/providers/claude/agent.js +257 -143
  18. package/dist/server/server/agent/providers/claude/models.js +7 -3
  19. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +4 -3
  20. package/dist/server/server/agent/providers/codex-app-server-agent.js +43 -1
  21. package/dist/server/server/agent/providers/copilot-acp-agent.js +4 -1
  22. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +9 -0
  23. package/dist/server/server/agent/providers/diagnostic-utils.js +188 -0
  24. package/dist/server/server/agent/providers/mock-slow-provider.js +1 -1
  25. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +29 -2
  26. package/dist/server/server/agent/providers/opencode/server-manager.js +83 -17
  27. package/dist/server/server/agent/providers/opencode-agent.d.ts +2 -0
  28. package/dist/server/server/agent/providers/opencode-agent.js +14 -9
  29. package/dist/server/server/agent/providers/pi/agent.js +27 -14
  30. package/dist/server/server/bootstrap.d.ts +2 -0
  31. package/dist/server/server/bootstrap.js +32 -2
  32. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  33. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  34. package/dist/server/server/resolve-worktree-creation-intent.d.ts +3 -0
  35. package/dist/server/server/resolve-worktree-creation-intent.js +3 -3
  36. package/dist/server/server/session.d.ts +12 -1
  37. package/dist/server/server/session.js +230 -40
  38. package/dist/server/server/speech/providers/openai/runtime.js +3 -4
  39. package/dist/server/server/websocket-server.d.ts +1 -0
  40. package/dist/server/server/websocket-server.js +11 -0
  41. package/dist/server/server/workspace-archive-service.js +2 -3
  42. package/dist/server/server/workspace-directory.js +5 -5
  43. package/dist/server/server/workspace-reconciliation-service.js +2 -2
  44. package/dist/server/server/worktree-core.d.ts +1 -0
  45. package/dist/server/server/worktree-core.js +5 -1
  46. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  47. package/dist/server/services/quota-fetcher/manifest.js +47 -0
  48. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  49. package/dist/server/services/quota-fetcher/provider.js +2 -0
  50. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  51. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  52. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  53. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  54. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  55. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  56. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  57. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  58. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  59. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  60. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  61. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  62. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  63. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  64. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  65. package/dist/server/services/quota-fetcher/service.js +58 -0
  66. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  67. package/dist/server/services/quota-fetcher/usage.js +49 -0
  68. package/dist/server/utils/directory-suggestions.js +98 -2
  69. package/package.json +5 -5
@@ -0,0 +1,47 @@
1
+ import { ClaudeQuotaProvider } from "./providers/claude.js";
2
+ import { CodexQuotaProvider } from "./providers/codex.js";
3
+ import { CopilotQuotaProvider } from "./providers/copilot.js";
4
+ import { CursorQuotaProvider } from "./providers/cursor.js";
5
+ import { GrokQuotaProvider } from "./providers/grok.js";
6
+ import { KimiQuotaProvider } from "./providers/kimi.js";
7
+ import { ZaiQuotaProvider } from "./providers/zai.js";
8
+ export const PROVIDER_USAGE_FETCHERS = [
9
+ {
10
+ providerId: "claude",
11
+ create: (options) => new ClaudeQuotaProvider({
12
+ logger: options.logger,
13
+ fetch: options.fetch,
14
+ }),
15
+ },
16
+ {
17
+ providerId: "codex",
18
+ create: (options) => new CodexQuotaProvider({
19
+ logger: options.logger,
20
+ fetch: options.fetch,
21
+ }),
22
+ },
23
+ {
24
+ providerId: "copilot",
25
+ create: (options) => new CopilotQuotaProvider({ logger: options.logger, fetch: options.fetch }),
26
+ },
27
+ {
28
+ providerId: "cursor",
29
+ create: (options) => new CursorQuotaProvider({ logger: options.logger, fetch: options.fetch }),
30
+ },
31
+ {
32
+ providerId: "zai",
33
+ create: (options) => new ZaiQuotaProvider({ logger: options.logger, fetch: options.fetch }),
34
+ },
35
+ {
36
+ providerId: "grok",
37
+ create: (options) => new GrokQuotaProvider({ logger: options.logger, fetch: options.fetch }),
38
+ },
39
+ {
40
+ providerId: "kimi",
41
+ create: (options) => new KimiQuotaProvider({ logger: options.logger, fetch: options.fetch }),
42
+ },
43
+ ];
44
+ export function createProviderUsageFetchers(options) {
45
+ return PROVIDER_USAGE_FETCHERS.map((entry) => entry.create(options));
46
+ }
47
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1,17 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../server/messages.js";
3
+ export type ProviderApiFetch = typeof fetch;
4
+ export interface ProviderUsageFetcher {
5
+ readonly providerId: string;
6
+ readonly displayName: string;
7
+ fetchUsage(): Promise<ProviderUsage>;
8
+ }
9
+ export interface ProviderUsageFetcherFactoryOptions {
10
+ logger: Logger;
11
+ fetch?: ProviderApiFetch;
12
+ }
13
+ export interface ProviderUsageFetcherManifestEntry {
14
+ readonly providerId: string;
15
+ create(options: ProviderUsageFetcherFactoryOptions): ProviderUsageFetcher;
16
+ }
17
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1,26 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../../server/messages.js";
3
+ import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
4
+ interface ClaudeQuotaProviderOptions {
5
+ logger: Logger;
6
+ claudeHome?: string;
7
+ claudeKeychainReader?: () => Promise<unknown | null>;
8
+ platform?: typeof process.platform;
9
+ fetch?: ProviderApiFetch;
10
+ }
11
+ export declare class ClaudeQuotaProvider implements ProviderUsageFetcher {
12
+ readonly providerId = "claude";
13
+ readonly displayName = "Claude";
14
+ private readonly claudeHome;
15
+ private readonly readKeychainCredentials;
16
+ private readonly platform;
17
+ private readonly fetchApi;
18
+ constructor(options: ClaudeQuotaProviderOptions);
19
+ fetchUsage(): Promise<ProviderUsage>;
20
+ private readCredentials;
21
+ private callClaudeApi;
22
+ private refreshClaudeToken;
23
+ private saveClaudeCredentials;
24
+ }
25
+ export {};
26
+ //# sourceMappingURL=claude.d.ts.map
@@ -0,0 +1,217 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync, promises as fs } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { z } from "zod";
7
+ import { ApiNumberSchema, fetchProviderApi, unavailableUsage, windowFromUsedPct, } from "../usage.js";
8
+ const execFileAsync = promisify(execFile);
9
+ const CLAUDE_KEYCHAIN_TIMEOUT_MS = 2000;
10
+ const CLAUDE_OAUTH_BETA = "oauth-2025-04-20";
11
+ const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
12
+ const CLAUDE_KEYCHAIN_SERVICE = "Claude Code-credentials";
13
+ const ClaudeCredentialsSchema = z.object({
14
+ claudeAiOauth: z
15
+ .object({
16
+ accessToken: z.string().optional(),
17
+ refreshToken: z.string().optional(),
18
+ subscriptionType: z.string().optional(),
19
+ rateLimitTier: z.string().optional(),
20
+ })
21
+ .optional(),
22
+ });
23
+ const ClaudeUsageWindowSchema = z.object({
24
+ utilization: ApiNumberSchema,
25
+ resets_at: z.string().optional(),
26
+ });
27
+ const ClaudeUsageResponseSchema = z.object({
28
+ five_hour: ClaudeUsageWindowSchema.nullish(),
29
+ seven_day: ClaudeUsageWindowSchema.nullish(),
30
+ seven_day_opus: ClaudeUsageWindowSchema.nullish(),
31
+ seven_day_omelette: ClaudeUsageWindowSchema.nullish(),
32
+ extra_usage: z
33
+ .object({
34
+ is_enabled: z.boolean().optional(),
35
+ })
36
+ .nullish(),
37
+ });
38
+ const ClaudeTokenRefreshSchema = z.object({
39
+ access_token: z.string().optional(),
40
+ refresh_token: z.string().optional(),
41
+ });
42
+ function buildClaudePlan(subscriptionType, rateLimitTier) {
43
+ if (!subscriptionType)
44
+ return null;
45
+ const label = subscriptionType.charAt(0).toUpperCase() + subscriptionType.slice(1);
46
+ const tier = rateLimitTier?.split("_").pop();
47
+ return tier ? `${label} ${tier}` : label;
48
+ }
49
+ async function readClaudeKeychainCredentials() {
50
+ try {
51
+ const { stdout } = await execFileAsync("security", ["find-generic-password", "-s", CLAUDE_KEYCHAIN_SERVICE, "-w"], { timeout: CLAUDE_KEYCHAIN_TIMEOUT_MS });
52
+ const raw = stdout.trim();
53
+ if (!raw)
54
+ return null;
55
+ return JSON.parse(raw);
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ export class ClaudeQuotaProvider {
62
+ constructor(options) {
63
+ this.providerId = "claude";
64
+ this.displayName = "Claude";
65
+ this.claudeHome =
66
+ options.claudeHome || process.env["CLAUDE_HOME"] || join(homedir(), ".claude");
67
+ this.readKeychainCredentials = options.claudeKeychainReader ?? readClaudeKeychainCredentials;
68
+ this.platform = options.platform ?? process.platform;
69
+ this.fetchApi = options.fetch ?? fetch;
70
+ }
71
+ async fetchUsage() {
72
+ const credentials = await this.readCredentials();
73
+ if (!credentials) {
74
+ return unavailableUsage(this);
75
+ }
76
+ const { oauth, filePath } = credentials;
77
+ const plan = buildClaudePlan(oauth.subscriptionType, oauth.rateLimitTier);
78
+ let resp = await this.callClaudeApi(oauth.accessToken);
79
+ if (resp === "NEEDS_AUTH") {
80
+ if (!filePath || !oauth.refreshToken) {
81
+ return unavailableUsage(this);
82
+ }
83
+ const refreshed = await this.refreshClaudeToken(oauth.refreshToken);
84
+ if (!refreshed?.access_token) {
85
+ return unavailableUsage(this);
86
+ }
87
+ await this.saveClaudeCredentials(filePath, {
88
+ ...oauth,
89
+ accessToken: refreshed.access_token,
90
+ refreshToken: refreshed.refresh_token ?? oauth.refreshToken,
91
+ });
92
+ resp = await this.callClaudeApi(refreshed.access_token);
93
+ if (resp === "NEEDS_AUTH") {
94
+ return unavailableUsage(this);
95
+ }
96
+ }
97
+ const windows = [];
98
+ if (resp.five_hour) {
99
+ windows.push(windowFromUsedPct({
100
+ id: "five_hour",
101
+ label: "Session",
102
+ utilizationPct: resp.five_hour.utilization,
103
+ resetsAt: resp.five_hour.resets_at ?? null,
104
+ tone: "ok",
105
+ }));
106
+ }
107
+ if (resp.seven_day) {
108
+ windows.push(windowFromUsedPct({
109
+ id: "weekly",
110
+ label: "Weekly",
111
+ utilizationPct: resp.seven_day.utilization,
112
+ resetsAt: resp.seven_day.resets_at ?? null,
113
+ tone: "ok",
114
+ }));
115
+ }
116
+ if (resp.seven_day_opus) {
117
+ windows.push(windowFromUsedPct({
118
+ id: "weekly_opus",
119
+ label: "Weekly · Opus",
120
+ utilizationPct: resp.seven_day_opus.utilization,
121
+ resetsAt: resp.seven_day_opus.resets_at ?? null,
122
+ tone: "ok",
123
+ }));
124
+ }
125
+ if (resp.seven_day_omelette) {
126
+ windows.push(windowFromUsedPct({
127
+ id: "weekly_omelette",
128
+ label: "Weekly · Omelette",
129
+ utilizationPct: resp.seven_day_omelette.utilization,
130
+ resetsAt: resp.seven_day_omelette.resets_at ?? null,
131
+ tone: "ok",
132
+ }));
133
+ }
134
+ const details = [];
135
+ const extraUsageEnabled = resp.extra_usage?.is_enabled;
136
+ if (extraUsageEnabled !== undefined) {
137
+ details.push({
138
+ id: "extra_usage",
139
+ label: "Extra usage",
140
+ value: extraUsageEnabled ? "Enabled" : "Disabled",
141
+ });
142
+ }
143
+ return {
144
+ providerId: this.providerId,
145
+ displayName: this.displayName,
146
+ status: "available",
147
+ planLabel: plan,
148
+ windows,
149
+ balances: [],
150
+ details,
151
+ error: null,
152
+ };
153
+ }
154
+ async readCredentials() {
155
+ const credPath = join(this.claudeHome, ".credentials.json");
156
+ if (existsSync(credPath)) {
157
+ try {
158
+ const creds = ClaudeCredentialsSchema.parse(JSON.parse(await fs.readFile(credPath, "utf8")));
159
+ const oauth = creds.claudeAiOauth;
160
+ if (oauth?.accessToken) {
161
+ return { oauth: { ...oauth, accessToken: oauth.accessToken }, filePath: credPath };
162
+ }
163
+ }
164
+ catch {
165
+ // Fall through to the macOS Keychain below.
166
+ }
167
+ }
168
+ if (this.platform === "darwin") {
169
+ const creds = ClaudeCredentialsSchema.safeParse(await this.readKeychainCredentials());
170
+ const oauth = creds.success ? creds.data.claudeAiOauth : undefined;
171
+ if (oauth?.accessToken) {
172
+ return { oauth: { ...oauth, accessToken: oauth.accessToken }, filePath: null };
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+ async callClaudeApi(token) {
178
+ const res = await fetchProviderApi(this.fetchApi, "https://api.anthropic.com/api/oauth/usage", {
179
+ headers: {
180
+ Authorization: `Bearer ${token}`,
181
+ Accept: "application/json",
182
+ "anthropic-beta": CLAUDE_OAUTH_BETA,
183
+ },
184
+ });
185
+ if (res.status === 401 || res.status === 403)
186
+ return "NEEDS_AUTH";
187
+ if (!res.ok)
188
+ throw new Error(`Claude usage API returned ${res.status}`);
189
+ return ClaudeUsageResponseSchema.parse(await res.json());
190
+ }
191
+ async refreshClaudeToken(refreshToken) {
192
+ const res = await fetchProviderApi(this.fetchApi, "https://platform.claude.com/v1/oauth/token", {
193
+ method: "POST",
194
+ headers: { "Content-Type": "application/json" },
195
+ body: JSON.stringify({
196
+ grant_type: "refresh_token",
197
+ refresh_token: refreshToken,
198
+ client_id: CLAUDE_CLIENT_ID,
199
+ scope: "user:profile user:inference user:sessions:claude_code user:mcp_servers",
200
+ }),
201
+ });
202
+ if (!res.ok)
203
+ return null;
204
+ return ClaudeTokenRefreshSchema.parse(await res.json());
205
+ }
206
+ async saveClaudeCredentials(credPath, oauth) {
207
+ try {
208
+ const existing = ClaudeCredentialsSchema.parse(JSON.parse(await fs.readFile(credPath, "utf8")));
209
+ existing.claudeAiOauth = oauth;
210
+ await fs.writeFile(credPath, JSON.stringify(existing, null, 2), { mode: 0o600 });
211
+ }
212
+ catch {
213
+ // Non-fatal; Claude Code can refresh again on its own next time.
214
+ }
215
+ }
216
+ }
217
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1,23 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../../server/messages.js";
3
+ import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
4
+ interface CodexQuotaProviderOptions {
5
+ logger: Logger;
6
+ codexHome?: string;
7
+ fetch?: ProviderApiFetch;
8
+ }
9
+ export declare class CodexQuotaProvider implements ProviderUsageFetcher {
10
+ readonly providerId = "codex";
11
+ readonly displayName = "Codex";
12
+ private readonly codexHome;
13
+ private readonly fetchApi;
14
+ constructor(options: CodexQuotaProviderOptions);
15
+ fetchUsage(): Promise<ProviderUsage>;
16
+ private toUsage;
17
+ private readCodexAuth;
18
+ private callCodexApi;
19
+ private refreshCodexToken;
20
+ private saveCodexAuth;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1,211 @@
1
+ import { existsSync, promises as fs } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
5
+ import { ApiNumberSchema, balanceToneFromRemaining, fetchProviderApi, unavailableUsage, windowFromUsedPct, } from "../usage.js";
6
+ const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
7
+ const CodexAuthSchema = z.object({
8
+ tokens: z
9
+ .object({
10
+ access_token: z.string().optional(),
11
+ refresh_token: z.string().optional(),
12
+ account_id: z.string().optional(),
13
+ })
14
+ .optional(),
15
+ });
16
+ const CodexWindowSchema = z.object({
17
+ used_percent: ApiNumberSchema.optional(),
18
+ reset_at: ApiNumberSchema.optional(),
19
+ });
20
+ const CodexUsageResponseSchema = z.object({
21
+ plan_type: z.string().optional(),
22
+ email: z.string().optional(),
23
+ rate_limit: z
24
+ .object({
25
+ primary_window: CodexWindowSchema.nullish(),
26
+ secondary_window: CodexWindowSchema.nullish(),
27
+ })
28
+ .nullish(),
29
+ code_review_rate_limit: z
30
+ .object({
31
+ primary_window: CodexWindowSchema.nullish(),
32
+ })
33
+ .nullish(),
34
+ credits: z
35
+ .object({
36
+ has_credits: z.boolean().optional(),
37
+ unlimited: z.boolean().optional(),
38
+ balance: ApiNumberSchema.optional(),
39
+ })
40
+ .nullish(),
41
+ });
42
+ const CodexTokenRefreshSchema = z.object({
43
+ access_token: z.string().optional(),
44
+ refresh_token: z.string().optional(),
45
+ });
46
+ function codexWindow(window) {
47
+ if (!window)
48
+ return null;
49
+ return {
50
+ usedPct: window.used_percent ?? 0,
51
+ resetsAt: window.reset_at != null ? new Date(window.reset_at * 1000).toISOString() : null,
52
+ };
53
+ }
54
+ export class CodexQuotaProvider {
55
+ constructor(options) {
56
+ this.providerId = "codex";
57
+ this.displayName = "Codex";
58
+ this.codexHome = options.codexHome || process.env["CODEX_HOME"] || join(homedir(), ".codex");
59
+ this.fetchApi = options.fetch ?? fetch;
60
+ }
61
+ async fetchUsage() {
62
+ const authRecord = await this.readCodexAuth();
63
+ const auth = authRecord?.auth;
64
+ const accessToken = auth?.tokens?.access_token;
65
+ if (!authRecord || !auth || !accessToken) {
66
+ return unavailableUsage(this);
67
+ }
68
+ const { refresh_token, account_id } = auth.tokens ?? {};
69
+ let resp = await this.callCodexApi(accessToken, account_id);
70
+ if (resp === "NEEDS_AUTH") {
71
+ if (!refresh_token) {
72
+ return unavailableUsage(this);
73
+ }
74
+ const refreshed = await this.refreshCodexToken(refresh_token);
75
+ if (!refreshed?.access_token) {
76
+ return unavailableUsage(this);
77
+ }
78
+ await this.saveCodexAuth(authRecord.path, auth, refreshed);
79
+ resp = await this.callCodexApi(refreshed.access_token, account_id);
80
+ if (resp === "NEEDS_AUTH") {
81
+ return unavailableUsage(this);
82
+ }
83
+ }
84
+ return this.toUsage(resp);
85
+ }
86
+ toUsage(resp) {
87
+ const session = codexWindow(resp.rate_limit?.primary_window);
88
+ const weekly = codexWindow(resp.rate_limit?.secondary_window);
89
+ const codeReview = codexWindow(resp.code_review_rate_limit?.primary_window);
90
+ const windows = [];
91
+ if (session) {
92
+ windows.push(windowFromUsedPct({
93
+ id: "session",
94
+ label: "Session",
95
+ utilizationPct: session.usedPct,
96
+ resetsAt: session.resetsAt,
97
+ tone: "ok",
98
+ }));
99
+ }
100
+ if (weekly) {
101
+ windows.push(windowFromUsedPct({
102
+ id: "weekly",
103
+ label: "Weekly",
104
+ utilizationPct: weekly.usedPct,
105
+ resetsAt: weekly.resetsAt,
106
+ tone: weekly.usedPct >= 70 ? "warning" : "ok",
107
+ }));
108
+ }
109
+ if (codeReview) {
110
+ windows.push(windowFromUsedPct({
111
+ id: "code_review",
112
+ label: "Code review",
113
+ utilizationPct: codeReview.usedPct,
114
+ resetsAt: codeReview.resetsAt,
115
+ tone: codeReview.usedPct >= 70 ? "warning" : "ok",
116
+ }));
117
+ }
118
+ const balances = [];
119
+ if (resp.credits?.balance !== undefined) {
120
+ balances.push({
121
+ id: "credits",
122
+ label: "Credits",
123
+ remaining: resp.credits.balance,
124
+ unit: "usd",
125
+ tone: balanceToneFromRemaining(resp.credits.balance),
126
+ });
127
+ }
128
+ return {
129
+ providerId: this.providerId,
130
+ displayName: this.displayName,
131
+ status: "available",
132
+ planLabel: resp.plan_type ?? null,
133
+ windows,
134
+ balances,
135
+ details: [],
136
+ error: null,
137
+ };
138
+ }
139
+ async readCodexAuth() {
140
+ const candidates = [
141
+ ...(process.env["CODEX_HOME"] ? [join(process.env["CODEX_HOME"], "auth.json")] : []),
142
+ join(homedir(), ".config", "codex", "auth.json"),
143
+ join(this.codexHome, "auth.json"),
144
+ ];
145
+ for (const path of candidates) {
146
+ if (!existsSync(path))
147
+ continue;
148
+ try {
149
+ const auth = CodexAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
150
+ if (auth.tokens?.access_token)
151
+ return { auth, path };
152
+ }
153
+ catch {
154
+ continue;
155
+ }
156
+ }
157
+ return null;
158
+ }
159
+ async callCodexApi(token, accountId) {
160
+ const headers = {
161
+ Authorization: `Bearer ${token}`,
162
+ Accept: "application/json",
163
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
164
+ };
165
+ if (accountId)
166
+ headers["ChatGPT-Account-Id"] = accountId;
167
+ const res = await fetchProviderApi(this.fetchApi, "https://chatgpt.com/backend-api/wham/usage", {
168
+ headers,
169
+ });
170
+ if (res.status === 401 || res.status === 403)
171
+ return "NEEDS_AUTH";
172
+ if (!res.ok)
173
+ throw new Error(`Codex usage API returned ${res.status}`);
174
+ const text = await res.text();
175
+ if (text.trim().startsWith("<"))
176
+ return "NEEDS_AUTH";
177
+ return CodexUsageResponseSchema.parse(JSON.parse(text));
178
+ }
179
+ async refreshCodexToken(refreshToken) {
180
+ const params = new URLSearchParams({
181
+ grant_type: "refresh_token",
182
+ client_id: CODEX_CLIENT_ID,
183
+ refresh_token: refreshToken,
184
+ });
185
+ const res = await fetchProviderApi(this.fetchApi, "https://auth.openai.com/oauth/token", {
186
+ method: "POST",
187
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
188
+ body: params.toString(),
189
+ });
190
+ if (!res.ok)
191
+ return null;
192
+ return CodexTokenRefreshSchema.parse(await res.json());
193
+ }
194
+ async saveCodexAuth(authPath, original, refreshed) {
195
+ try {
196
+ const updated = {
197
+ ...original,
198
+ tokens: {
199
+ ...original.tokens,
200
+ access_token: refreshed.access_token ?? original.tokens?.access_token,
201
+ refresh_token: refreshed.refresh_token ?? original.tokens?.refresh_token,
202
+ },
203
+ };
204
+ await fs.writeFile(authPath, JSON.stringify(updated, null, 2), { mode: 0o600 });
205
+ }
206
+ catch {
207
+ // Non-fatal; the next call can refresh again.
208
+ }
209
+ }
210
+ }
211
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1,17 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../../server/messages.js";
3
+ import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
4
+ interface CopilotQuotaProviderOptions {
5
+ logger: Logger;
6
+ fetch?: ProviderApiFetch;
7
+ }
8
+ export declare class CopilotQuotaProvider implements ProviderUsageFetcher {
9
+ readonly providerId = "copilot";
10
+ readonly displayName = "GitHub Copilot";
11
+ private readonly logger;
12
+ private readonly fetchApi;
13
+ constructor(options: CopilotQuotaProviderOptions);
14
+ fetchUsage(): Promise<ProviderUsage>;
15
+ }
16
+ export {};
17
+ //# sourceMappingURL=copilot.d.ts.map
@@ -0,0 +1,75 @@
1
+ import { existsSync, promises as fs } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
5
+ import { ApiOptionalStringSchema, fetchProviderApi, unavailableUsage } from "../usage.js";
6
+ const CopilotUsageResponseSchema = z.object({
7
+ copilot_plan: ApiOptionalStringSchema,
8
+ quota_reset_date: ApiOptionalStringSchema,
9
+ });
10
+ async function readGithubCliToken() {
11
+ const candidates = [];
12
+ if (process.env["APPDATA"]) {
13
+ candidates.push(join(process.env["APPDATA"], "GitHub CLI", "hosts.yml"));
14
+ }
15
+ candidates.push(join(homedir(), ".config", "gh", "hosts.yml"));
16
+ for (const path of candidates) {
17
+ if (!existsSync(path))
18
+ continue;
19
+ try {
20
+ const raw = await fs.readFile(path, "utf8");
21
+ const match = raw.match(/oauth_token:\s*["']?([a-zA-Z0-9_-]+)["']?/);
22
+ if (match?.[1])
23
+ return match[1];
24
+ }
25
+ catch {
26
+ continue;
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+ export class CopilotQuotaProvider {
32
+ constructor(options) {
33
+ this.providerId = "copilot";
34
+ this.displayName = "GitHub Copilot";
35
+ this.logger = options.logger;
36
+ this.fetchApi = options.fetch ?? fetch;
37
+ }
38
+ async fetchUsage() {
39
+ const token = process.env["COPILOT_TOKEN"] ||
40
+ process.env["GITHUB_TOKEN"] ||
41
+ process.env["GITHUB_PAT"] ||
42
+ (await readGithubCliToken());
43
+ if (!token)
44
+ return unavailableUsage(this);
45
+ const res = await fetchProviderApi(this.fetchApi, "https://api.github.com/copilot_internal/user", {
46
+ headers: {
47
+ Authorization: `token ${token}`,
48
+ Accept: "application/json",
49
+ "Editor-Version": "vscode/1.96.2",
50
+ "Editor-Plugin-Version": "copilot-chat/0.26.7",
51
+ "User-Agent": "GitHubCopilotChat/0.26.7",
52
+ "X-Github-Api-Version": "2025-04-01",
53
+ },
54
+ });
55
+ if (!res.ok) {
56
+ this.logger.debug({ status: res.status }, "Copilot usage fetch failed");
57
+ return unavailableUsage(this);
58
+ }
59
+ const resp = CopilotUsageResponseSchema.parse(await res.json());
60
+ const details = resp.quota_reset_date
61
+ ? [{ id: "reset", label: "Quota reset", value: resp.quota_reset_date }]
62
+ : [];
63
+ return {
64
+ providerId: this.providerId,
65
+ displayName: this.displayName,
66
+ status: "available",
67
+ planLabel: resp.copilot_plan || null,
68
+ windows: [],
69
+ balances: [],
70
+ details,
71
+ error: null,
72
+ };
73
+ }
74
+ }
75
+ //# sourceMappingURL=copilot.js.map
@@ -0,0 +1,17 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../../server/messages.js";
3
+ import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
4
+ interface CursorQuotaProviderOptions {
5
+ logger: Logger;
6
+ fetch?: ProviderApiFetch;
7
+ }
8
+ export declare class CursorQuotaProvider implements ProviderUsageFetcher {
9
+ readonly providerId = "cursor";
10
+ readonly displayName = "Cursor";
11
+ private readonly logger;
12
+ private readonly fetchApi;
13
+ constructor(options: CursorQuotaProviderOptions);
14
+ fetchUsage(): Promise<ProviderUsage>;
15
+ }
16
+ export {};
17
+ //# sourceMappingURL=cursor.d.ts.map