@getpaseo/server 0.1.97-beta.3 → 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 (96) hide show
  1. package/dist/server/server/agent/agent-manager.d.ts +11 -3
  2. package/dist/server/server/agent/agent-manager.js +95 -23
  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-response-loop.js +9 -3
  6. package/dist/server/server/agent/agent-sdk-types.d.ts +9 -3
  7. package/dist/server/server/agent/agent-storage.d.ts +20 -240
  8. package/dist/server/server/agent/agent-storage.js +6 -6
  9. package/dist/server/server/agent/create-agent/create.d.ts +2 -0
  10. package/dist/server/server/agent/create-agent/create.js +8 -7
  11. package/dist/server/server/agent/lifecycle-command.d.ts +15 -1
  12. package/dist/server/server/agent/lifecycle-command.js +9 -2
  13. package/dist/server/server/agent/mcp-server.js +263 -119
  14. package/dist/server/server/agent/mcp-shared.d.ts +35 -179
  15. package/dist/server/server/agent/provider-notices.d.ts +3 -0
  16. package/dist/server/server/agent/provider-notices.js +5 -0
  17. package/dist/server/server/agent/provider-registry.d.ts +2 -0
  18. package/dist/server/server/agent/provider-registry.js +10 -3
  19. package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -0
  20. package/dist/server/server/agent/provider-snapshot-manager.js +11 -2
  21. package/dist/server/server/agent/providers/claude/agent.js +257 -143
  22. package/dist/server/server/agent/providers/claude/models.js +7 -3
  23. package/dist/server/server/agent/providers/claude/project-dir.js +9 -6
  24. package/dist/server/server/agent/providers/claude/task-notification-tool-call.d.ts +2 -22
  25. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +8 -118
  26. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +4 -3
  27. package/dist/server/server/agent/providers/codex-app-server-agent.js +43 -1
  28. package/dist/server/server/agent/providers/copilot-acp-agent.js +4 -1
  29. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +9 -0
  30. package/dist/server/server/agent/providers/diagnostic-utils.js +188 -0
  31. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +1 -5
  32. package/dist/server/server/agent/providers/mock-slow-provider.js +1 -1
  33. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +29 -2
  34. package/dist/server/server/agent/providers/opencode/server-manager.js +83 -17
  35. package/dist/server/server/agent/providers/opencode-agent.d.ts +2 -0
  36. package/dist/server/server/agent/providers/opencode-agent.js +14 -9
  37. package/dist/server/server/agent/providers/pi/agent.d.ts +1 -5
  38. package/dist/server/server/agent/providers/pi/agent.js +27 -14
  39. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +391 -1261
  40. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +26 -16
  41. package/dist/server/server/bootstrap.d.ts +2 -0
  42. package/dist/server/server/bootstrap.js +32 -2
  43. package/dist/server/server/loop-service.d.ts +60 -359
  44. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  45. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  46. package/dist/server/server/migrations/backfill-workspace-id.migration.js +10 -6
  47. package/dist/server/server/package-version.d.ts +1 -7
  48. package/dist/server/server/paseo-worktree-service.js +15 -1
  49. package/dist/server/server/persisted-config.d.ts +138 -1009
  50. package/dist/server/server/persisted-config.js +1 -1
  51. package/dist/server/server/pid-lock.d.ts +1 -15
  52. package/dist/server/server/resolve-worktree-creation-intent.d.ts +3 -0
  53. package/dist/server/server/resolve-worktree-creation-intent.js +3 -3
  54. package/dist/server/server/session.d.ts +18 -1
  55. package/dist/server/server/session.js +424 -64
  56. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts +2 -2
  57. package/dist/server/server/speech/providers/openai/runtime.js +3 -4
  58. package/dist/server/server/speech/speech-types.d.ts +9 -11
  59. package/dist/server/server/websocket-server.d.ts +1 -0
  60. package/dist/server/server/websocket-server.js +15 -0
  61. package/dist/server/server/workspace-archive-service.js +2 -3
  62. package/dist/server/server/workspace-directory.js +5 -5
  63. package/dist/server/server/workspace-reconciliation-service.js +2 -2
  64. package/dist/server/server/workspace-registry.d.ts +17 -48
  65. package/dist/server/server/workspace-registry.js +9 -0
  66. package/dist/server/server/worktree-core.d.ts +1 -0
  67. package/dist/server/server/worktree-core.js +5 -1
  68. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  69. package/dist/server/services/quota-fetcher/manifest.js +47 -0
  70. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  71. package/dist/server/services/quota-fetcher/provider.js +2 -0
  72. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  73. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  74. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  75. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  76. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  77. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  78. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  79. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  80. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  81. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  82. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  83. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  84. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  85. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  86. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  87. package/dist/server/services/quota-fetcher/service.js +58 -0
  88. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  89. package/dist/server/services/quota-fetcher/usage.js +49 -0
  90. package/dist/server/terminal/terminal-session-controller.d.ts +8 -0
  91. package/dist/server/terminal/terminal-session-controller.js +23 -3
  92. package/dist/server/utils/checkout-git.js +36 -76
  93. package/dist/server/utils/directory-suggestions.js +98 -2
  94. package/dist/server/utils/worktree-metadata.d.ts +7 -59
  95. package/dist/src/server/persisted-config.js +1 -1
  96. package/package.json +9 -9
@@ -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
@@ -0,0 +1,123 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync } 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 { ApiNullableNumberSchema, balanceToneFromRemaining, fetchProviderApi, toIsoStringOrNull, unavailableUsage, } from "../usage.js";
8
+ const execFileAsync = promisify(execFile);
9
+ const CURSOR_SQLITE_TIMEOUT_MS = 2000;
10
+ const CursorBillingCycleTimestampSchema = z.preprocess((value) => (typeof value === "string" || typeof value === "number" ? value : null), z.union([z.string(), z.number()]).nullable());
11
+ const CursorUsageResponseSchema = z.object({
12
+ planUsage: z
13
+ .object({
14
+ totalSpend: ApiNullableNumberSchema,
15
+ includedSpend: ApiNullableNumberSchema,
16
+ bonusSpend: ApiNullableNumberSchema,
17
+ remaining: ApiNullableNumberSchema,
18
+ limit: ApiNullableNumberSchema,
19
+ })
20
+ .nullish(),
21
+ billingCycleStart: CursorBillingCycleTimestampSchema,
22
+ billingCycleEnd: CursorBillingCycleTimestampSchema,
23
+ });
24
+ const CursorAuthStatusSchema = z.object({
25
+ accessToken: z.string().optional(),
26
+ });
27
+ function parseCursorBillingCycleTimestamp(value) {
28
+ if (value === null)
29
+ return null;
30
+ const raw = String(value).trim();
31
+ if (!raw)
32
+ return null;
33
+ const numeric = Number(raw);
34
+ if (Number.isFinite(numeric)) {
35
+ const timestampMs = Math.abs(numeric) < 10000000000 ? numeric * 1000 : numeric;
36
+ return toIsoStringOrNull(timestampMs);
37
+ }
38
+ return toIsoStringOrNull(new Date(raw).getTime());
39
+ }
40
+ function centsToDollars(value) {
41
+ return value === null ? null : value / 100;
42
+ }
43
+ async function readCursorTokenFromSqlite() {
44
+ const dbPaths = [];
45
+ if (process.env["APPDATA"]) {
46
+ dbPaths.push(join(process.env["APPDATA"], "Cursor", "User", "globalStorage", "state.vscdb"));
47
+ }
48
+ dbPaths.push(join(homedir(), "Library", "Application Support", "Cursor", "User", "globalStorage", "state.vscdb"));
49
+ dbPaths.push(join(homedir(), ".config", "Cursor", "User", "globalStorage", "state.vscdb"));
50
+ for (const path of dbPaths) {
51
+ if (!existsSync(path))
52
+ continue;
53
+ try {
54
+ const { stdout } = await execFileAsync("sqlite3", [path, "SELECT value FROM ItemTable WHERE key = 'cursorAuthStatus'"], { timeout: CURSOR_SQLITE_TIMEOUT_MS });
55
+ if (stdout) {
56
+ const parsed = CursorAuthStatusSchema.parse(JSON.parse(stdout.trim()));
57
+ if (parsed.accessToken)
58
+ return parsed.accessToken;
59
+ }
60
+ }
61
+ catch {
62
+ continue;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ export class CursorQuotaProvider {
68
+ constructor(options) {
69
+ this.providerId = "cursor";
70
+ this.displayName = "Cursor";
71
+ this.logger = options.logger;
72
+ this.fetchApi = options.fetch ?? fetch;
73
+ }
74
+ async fetchUsage() {
75
+ const token = process.env["CURSOR_ACCESS_TOKEN"] ||
76
+ process.env["CURSOR_TOKEN"] ||
77
+ (await readCursorTokenFromSqlite());
78
+ if (!token)
79
+ return unavailableUsage(this);
80
+ const res = await fetchProviderApi(this.fetchApi, "https://api2.cursor.sh/aiserver.v1.DashboardService/GetCurrentPeriodUsage", {
81
+ method: "POST",
82
+ headers: {
83
+ Authorization: `Bearer ${token}`,
84
+ "Content-Type": "application/json",
85
+ "Connect-Protocol-Version": "1",
86
+ },
87
+ body: JSON.stringify({}),
88
+ });
89
+ if (!res.ok) {
90
+ this.logger.debug({ status: res.status }, "Cursor usage fetch failed");
91
+ return unavailableUsage(this);
92
+ }
93
+ const resp = CursorUsageResponseSchema.parse(await res.json());
94
+ const billingCycleEnd = parseCursorBillingCycleTimestamp(resp.billingCycleEnd);
95
+ const balances = [];
96
+ if (resp.planUsage) {
97
+ const totalSpend = centsToDollars(resp.planUsage.totalSpend);
98
+ const remaining = centsToDollars(resp.planUsage.remaining);
99
+ const limit = centsToDollars(resp.planUsage.limit);
100
+ balances.push({
101
+ id: "plan_usage",
102
+ label: "Plan usage",
103
+ used: totalSpend,
104
+ remaining,
105
+ limit,
106
+ unit: "usd",
107
+ resetsAt: billingCycleEnd,
108
+ tone: balanceToneFromRemaining(remaining),
109
+ });
110
+ }
111
+ return {
112
+ providerId: this.providerId,
113
+ displayName: this.displayName,
114
+ status: "available",
115
+ planLabel: null,
116
+ windows: [],
117
+ balances,
118
+ details: [],
119
+ error: null,
120
+ };
121
+ }
122
+ }
123
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1,18 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../../server/messages.js";
3
+ import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
4
+ interface GrokQuotaProviderOptions {
5
+ logger: Logger;
6
+ fetch?: ProviderApiFetch;
7
+ }
8
+ export declare class GrokQuotaProvider implements ProviderUsageFetcher {
9
+ readonly providerId = "grok";
10
+ readonly displayName = "Grok";
11
+ private readonly logger;
12
+ private readonly fetchApi;
13
+ constructor(options: GrokQuotaProviderOptions);
14
+ fetchUsage(): Promise<ProviderUsage>;
15
+ private readGrokToken;
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=grok.d.ts.map
@@ -0,0 +1,89 @@
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, } from "../usage.js";
6
+ const GrokUsageResponseSchema = z.object({
7
+ config: z
8
+ .object({
9
+ monthlyLimit: z
10
+ .object({
11
+ val: ApiNumberSchema.optional(),
12
+ })
13
+ .nullish(),
14
+ })
15
+ .nullish(),
16
+ usage: z
17
+ .object({
18
+ creditUsage: ApiNumberSchema.optional(),
19
+ })
20
+ .nullish(),
21
+ });
22
+ const GrokAuthSchema = z.object({
23
+ access_token: z.string().optional(),
24
+ });
25
+ export class GrokQuotaProvider {
26
+ constructor(options) {
27
+ this.providerId = "grok";
28
+ this.displayName = "Grok";
29
+ this.logger = options.logger;
30
+ this.fetchApi = options.fetch ?? fetch;
31
+ }
32
+ async fetchUsage() {
33
+ const token = process.env["GROK_API_KEY"] || process.env["GROK_TOKEN"] || (await this.readGrokToken());
34
+ if (!token)
35
+ return unavailableUsage(this);
36
+ const res = await fetchProviderApi(this.fetchApi, "https://cli-chat-proxy.grok.com/v1/billing", {
37
+ headers: {
38
+ Authorization: `Bearer ${token}`,
39
+ "X-XAI-Token-Auth": "xai-grok-cli",
40
+ Accept: "application/json",
41
+ },
42
+ });
43
+ if (!res.ok) {
44
+ this.logger.debug({ status: res.status }, "Grok usage fetch failed");
45
+ return unavailableUsage(this);
46
+ }
47
+ const resp = GrokUsageResponseSchema.parse(await res.json());
48
+ const monthlyLimit = resp.config?.monthlyLimit?.val ?? null;
49
+ const creditUsage = resp.usage?.creditUsage ?? null;
50
+ const balances = [];
51
+ if (monthlyLimit !== null || creditUsage !== null) {
52
+ const remaining = monthlyLimit !== null && creditUsage !== null
53
+ ? Math.max(0, monthlyLimit - creditUsage)
54
+ : null;
55
+ balances.push({
56
+ id: "monthly_credits",
57
+ label: "Monthly credits",
58
+ used: creditUsage,
59
+ remaining,
60
+ limit: monthlyLimit,
61
+ unit: "credits",
62
+ tone: balanceToneFromRemaining(remaining),
63
+ });
64
+ }
65
+ return {
66
+ providerId: this.providerId,
67
+ displayName: this.displayName,
68
+ status: "available",
69
+ planLabel: null,
70
+ windows: [],
71
+ balances,
72
+ details: [],
73
+ error: null,
74
+ };
75
+ }
76
+ async readGrokToken() {
77
+ const path = join(homedir(), ".grok", "auth.json");
78
+ if (!existsSync(path))
79
+ return null;
80
+ try {
81
+ const auth = GrokAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
82
+ return auth.access_token ?? null;
83
+ }
84
+ catch {
85
+ return null;
86
+ }
87
+ }
88
+ }
89
+ //# sourceMappingURL=grok.js.map
@@ -0,0 +1,20 @@
1
+ import type { Logger } from "pino";
2
+ import type { ProviderUsage } from "../../../server/messages.js";
3
+ import type { ProviderApiFetch, ProviderUsageFetcher } from "../provider.js";
4
+ interface KimiQuotaProviderOptions {
5
+ logger: Logger;
6
+ fetch?: ProviderApiFetch;
7
+ homeDir?: string;
8
+ }
9
+ export declare class KimiQuotaProvider implements ProviderUsageFetcher {
10
+ readonly providerId = "kimi";
11
+ readonly displayName = "Kimi";
12
+ private readonly logger;
13
+ private readonly fetchApi;
14
+ private readonly homeDir?;
15
+ constructor(options: KimiQuotaProviderOptions);
16
+ fetchUsage(): Promise<ProviderUsage>;
17
+ private readKimiToken;
18
+ }
19
+ export {};
20
+ //# sourceMappingURL=kimi.d.ts.map
@@ -0,0 +1,89 @@
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 KimiUsageResponseSchema = z.object({
7
+ usage: z
8
+ .object({
9
+ limit: ApiOptionalStringSchema,
10
+ remaining: ApiOptionalStringSchema,
11
+ resetTime: ApiOptionalStringSchema,
12
+ })
13
+ .nullish(),
14
+ });
15
+ const KimiAuthSchema = z.object({
16
+ access_token: z.string().optional(),
17
+ });
18
+ export class KimiQuotaProvider {
19
+ constructor(options) {
20
+ this.providerId = "kimi";
21
+ this.displayName = "Kimi";
22
+ this.logger = options.logger;
23
+ this.fetchApi = options.fetch ?? fetch;
24
+ this.homeDir = options.homeDir;
25
+ }
26
+ async fetchUsage() {
27
+ const token = process.env["KIMI_TOKEN"] || process.env["KIMI_API_KEY"] || (await this.readKimiToken());
28
+ if (!token)
29
+ return unavailableUsage(this);
30
+ const res = await fetchProviderApi(this.fetchApi, "https://api.kimi.com/coding/v1/usages", {
31
+ headers: {
32
+ Authorization: `Bearer ${token}`,
33
+ Accept: "application/json",
34
+ },
35
+ });
36
+ if (!res.ok) {
37
+ this.logger.debug({ status: res.status }, "Kimi usage fetch failed");
38
+ return unavailableUsage(this);
39
+ }
40
+ const resp = KimiUsageResponseSchema.parse(await res.json());
41
+ const limit = resp.usage?.limit === undefined ? null : Number(resp.usage.limit);
42
+ const remaining = resp.usage?.remaining === undefined ? null : Number(resp.usage.remaining);
43
+ const hasFiniteLimit = typeof limit === "number" && Number.isFinite(limit) && limit > 0;
44
+ const hasFiniteRemaining = typeof remaining === "number" && Number.isFinite(remaining);
45
+ const usedPct = hasFiniteLimit && hasFiniteRemaining
46
+ ? Math.max(0, Math.min(100, ((limit - remaining) / limit) * 100))
47
+ : null;
48
+ return {
49
+ providerId: this.providerId,
50
+ displayName: this.displayName,
51
+ status: "available",
52
+ planLabel: null,
53
+ windows: [
54
+ {
55
+ id: "coding_usage",
56
+ label: "Coding usage",
57
+ usedPct,
58
+ remainingPct: usedPct === null ? null : Math.max(0, 100 - usedPct),
59
+ resetsAt: resp.usage?.resetTime ?? null,
60
+ tone: "ok",
61
+ },
62
+ ],
63
+ balances: [],
64
+ details: [],
65
+ error: null,
66
+ };
67
+ }
68
+ async readKimiToken() {
69
+ const homeDir = this.homeDir ?? homedir();
70
+ const paths = [
71
+ join(process.env["KIMI_CODE_HOME"] || join(homeDir, ".kimi-code"), "credentials", "kimi-code.json"),
72
+ join(homeDir, ".kimi", "credentials", "kimi-code.json"),
73
+ ];
74
+ for (const path of paths) {
75
+ if (!existsSync(path))
76
+ continue;
77
+ try {
78
+ const credentials = KimiAuthSchema.parse(JSON.parse(await fs.readFile(path, "utf8")));
79
+ if (credentials.access_token)
80
+ return credentials.access_token;
81
+ }
82
+ catch {
83
+ continue;
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+ }
89
+ //# sourceMappingURL=kimi.js.map