@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.
- package/dist/server/server/agent/agent-manager.d.ts +11 -3
- package/dist/server/server/agent/agent-manager.js +94 -22
- package/dist/server/server/agent/agent-prompt.d.ts +1 -1
- package/dist/server/server/agent/agent-prompt.js +3 -10
- package/dist/server/server/agent/agent-sdk-types.d.ts +9 -3
- package/dist/server/server/agent/create-agent/create.d.ts +2 -0
- package/dist/server/server/agent/create-agent/create.js +8 -7
- package/dist/server/server/agent/lifecycle-command.d.ts +15 -1
- package/dist/server/server/agent/lifecycle-command.js +9 -2
- package/dist/server/server/agent/mcp-server.js +254 -115
- package/dist/server/server/agent/provider-notices.d.ts +3 -0
- package/dist/server/server/agent/provider-notices.js +5 -0
- package/dist/server/server/agent/provider-registry.d.ts +2 -0
- package/dist/server/server/agent/provider-registry.js +10 -3
- package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -0
- package/dist/server/server/agent/provider-snapshot-manager.js +11 -2
- package/dist/server/server/agent/providers/claude/agent.js +257 -143
- package/dist/server/server/agent/providers/claude/models.js +7 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +4 -3
- package/dist/server/server/agent/providers/codex-app-server-agent.js +43 -1
- package/dist/server/server/agent/providers/copilot-acp-agent.js +4 -1
- package/dist/server/server/agent/providers/diagnostic-utils.d.ts +9 -0
- package/dist/server/server/agent/providers/diagnostic-utils.js +188 -0
- package/dist/server/server/agent/providers/mock-slow-provider.js +1 -1
- package/dist/server/server/agent/providers/opencode/server-manager.d.ts +29 -2
- package/dist/server/server/agent/providers/opencode/server-manager.js +83 -17
- package/dist/server/server/agent/providers/opencode-agent.d.ts +2 -0
- package/dist/server/server/agent/providers/opencode-agent.js +14 -9
- package/dist/server/server/agent/providers/pi/agent.js +27 -14
- package/dist/server/server/bootstrap.d.ts +2 -0
- package/dist/server/server/bootstrap.js +32 -2
- package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
- package/dist/server/server/managed-processes/managed-processes.js +326 -0
- package/dist/server/server/resolve-worktree-creation-intent.d.ts +3 -0
- package/dist/server/server/resolve-worktree-creation-intent.js +3 -3
- package/dist/server/server/session.d.ts +12 -1
- package/dist/server/server/session.js +230 -40
- package/dist/server/server/speech/providers/openai/runtime.js +3 -4
- package/dist/server/server/websocket-server.d.ts +1 -0
- package/dist/server/server/websocket-server.js +11 -0
- package/dist/server/server/workspace-archive-service.js +2 -3
- package/dist/server/server/workspace-directory.js +5 -5
- package/dist/server/server/workspace-reconciliation-service.js +2 -2
- package/dist/server/server/worktree-core.d.ts +1 -0
- package/dist/server/server/worktree-core.js +5 -1
- package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
- package/dist/server/services/quota-fetcher/manifest.js +47 -0
- package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
- package/dist/server/services/quota-fetcher/provider.js +2 -0
- package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
- package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
- package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
- package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
- package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
- package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
- package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
- package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
- package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
- package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
- package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
- package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
- package/dist/server/services/quota-fetcher/service.d.ts +28 -0
- package/dist/server/services/quota-fetcher/service.js +58 -0
- package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
- package/dist/server/services/quota-fetcher/usage.js +49 -0
- package/dist/server/utils/directory-suggestions.js +98 -2
- package/package.json +5 -5
|
@@ -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
|
|
@@ -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 ZaiQuotaProviderOptions {
|
|
5
|
+
logger: Logger;
|
|
6
|
+
fetch?: ProviderApiFetch;
|
|
7
|
+
}
|
|
8
|
+
export declare class ZaiQuotaProvider implements ProviderUsageFetcher {
|
|
9
|
+
readonly providerId = "zai";
|
|
10
|
+
readonly displayName = "Z.ai";
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private readonly fetchApi;
|
|
13
|
+
constructor(options: ZaiQuotaProviderOptions);
|
|
14
|
+
fetchUsage(): Promise<ProviderUsage>;
|
|
15
|
+
}
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=zai.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ApiOptionalStringSchema, fetchProviderApi, unavailableUsage } from "../usage.js";
|
|
3
|
+
const ZaiUsageResponseSchema = z.object({
|
|
4
|
+
data: z
|
|
5
|
+
.array(z.object({
|
|
6
|
+
productName: ApiOptionalStringSchema,
|
|
7
|
+
status: ApiOptionalStringSchema,
|
|
8
|
+
purchaseTime: ApiOptionalStringSchema,
|
|
9
|
+
valid: ApiOptionalStringSchema,
|
|
10
|
+
}))
|
|
11
|
+
.optional(),
|
|
12
|
+
});
|
|
13
|
+
export class ZaiQuotaProvider {
|
|
14
|
+
constructor(options) {
|
|
15
|
+
this.providerId = "zai";
|
|
16
|
+
this.displayName = "Z.ai";
|
|
17
|
+
this.logger = options.logger;
|
|
18
|
+
this.fetchApi = options.fetch ?? fetch;
|
|
19
|
+
}
|
|
20
|
+
async fetchUsage() {
|
|
21
|
+
const token = process.env["ZAI_API_KEY"] || process.env["GLM_API_KEY"];
|
|
22
|
+
if (!token)
|
|
23
|
+
return unavailableUsage(this);
|
|
24
|
+
const res = await fetchProviderApi(this.fetchApi, "https://api.z.ai/api/biz/subscription/list", {
|
|
25
|
+
headers: {
|
|
26
|
+
Authorization: `Bearer ${token}`,
|
|
27
|
+
Accept: "application/json",
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
this.logger.debug({ status: res.status }, "Z.ai usage fetch failed");
|
|
32
|
+
return unavailableUsage(this);
|
|
33
|
+
}
|
|
34
|
+
const resp = ZaiUsageResponseSchema.parse(await res.json());
|
|
35
|
+
const sub = resp.data?.[0];
|
|
36
|
+
if (!sub)
|
|
37
|
+
return unavailableUsage(this);
|
|
38
|
+
const details = [];
|
|
39
|
+
if (sub.status)
|
|
40
|
+
details.push({ id: "status", label: "Status", value: sub.status });
|
|
41
|
+
if (sub.valid)
|
|
42
|
+
details.push({ id: "valid", label: "Valid", value: sub.valid });
|
|
43
|
+
if (sub.purchaseTime) {
|
|
44
|
+
details.push({ id: "purchase_time", label: "Purchased", value: sub.purchaseTime });
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
providerId: this.providerId,
|
|
48
|
+
displayName: this.displayName,
|
|
49
|
+
status: "available",
|
|
50
|
+
planLabel: sub.productName || null,
|
|
51
|
+
windows: [],
|
|
52
|
+
balances: [],
|
|
53
|
+
details,
|
|
54
|
+
error: null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=zai.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
import type { ProviderUsage } from "../../server/messages.js";
|
|
3
|
+
import type { ProviderApiFetch, ProviderUsageFetcher } from "./provider.js";
|
|
4
|
+
export interface ProviderUsageServiceOptions {
|
|
5
|
+
logger: Logger;
|
|
6
|
+
fetchers?: ProviderUsageFetcher[];
|
|
7
|
+
fetch?: ProviderApiFetch;
|
|
8
|
+
cacheTtlMs?: number;
|
|
9
|
+
now?: () => number;
|
|
10
|
+
}
|
|
11
|
+
export interface ProviderUsageListResult {
|
|
12
|
+
fetchedAt: string;
|
|
13
|
+
providers: ProviderUsage[];
|
|
14
|
+
}
|
|
15
|
+
export declare class ProviderUsageService {
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private readonly fetchers;
|
|
18
|
+
private readonly cacheTtlMs;
|
|
19
|
+
private readonly now;
|
|
20
|
+
private cached;
|
|
21
|
+
private inFlight;
|
|
22
|
+
constructor(options: ProviderUsageServiceOptions);
|
|
23
|
+
listUsage(options?: {
|
|
24
|
+
forceRefresh?: boolean;
|
|
25
|
+
}): Promise<ProviderUsageListResult>;
|
|
26
|
+
private fetchFreshUsage;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createProviderUsageFetchers } from "./manifest.js";
|
|
2
|
+
import { unavailableUsage } from "./usage.js";
|
|
3
|
+
const DEFAULT_PROVIDER_USAGE_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
4
|
+
export class ProviderUsageService {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.cached = null;
|
|
7
|
+
this.inFlight = null;
|
|
8
|
+
this.logger = options.logger.child({ module: "provider-usage-service" });
|
|
9
|
+
this.fetchers =
|
|
10
|
+
options.fetchers ??
|
|
11
|
+
createProviderUsageFetchers({
|
|
12
|
+
logger: this.logger,
|
|
13
|
+
fetch: options.fetch,
|
|
14
|
+
});
|
|
15
|
+
this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_PROVIDER_USAGE_CACHE_TTL_MS;
|
|
16
|
+
this.now = options.now ?? Date.now;
|
|
17
|
+
}
|
|
18
|
+
async listUsage(options) {
|
|
19
|
+
const nowMs = this.now();
|
|
20
|
+
if (!options?.forceRefresh &&
|
|
21
|
+
this.cached &&
|
|
22
|
+
nowMs - this.cached.fetchedAtMs < this.cacheTtlMs) {
|
|
23
|
+
return this.cached.result;
|
|
24
|
+
}
|
|
25
|
+
if (this.inFlight) {
|
|
26
|
+
return this.inFlight;
|
|
27
|
+
}
|
|
28
|
+
const request = this.fetchFreshUsage(nowMs);
|
|
29
|
+
this.inFlight = request;
|
|
30
|
+
try {
|
|
31
|
+
return await request;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
if (this.inFlight === request) {
|
|
35
|
+
this.inFlight = null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async fetchFreshUsage(nowMs) {
|
|
40
|
+
const settled = await Promise.allSettled(this.fetchers.map((fetcher) => fetcher.fetchUsage()));
|
|
41
|
+
const providers = settled.map((result, index) => {
|
|
42
|
+
const fetcher = this.fetchers[index];
|
|
43
|
+
if (result.status === "fulfilled") {
|
|
44
|
+
return result.value;
|
|
45
|
+
}
|
|
46
|
+
this.logger.debug({ err: result.reason, providerId: fetcher.providerId }, "Provider usage fetch failed");
|
|
47
|
+
return unavailableUsage({
|
|
48
|
+
providerId: fetcher.providerId,
|
|
49
|
+
displayName: fetcher.displayName,
|
|
50
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason),
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
const result = { fetchedAt: new Date(nowMs).toISOString(), providers };
|
|
54
|
+
this.cached = { fetchedAtMs: nowMs, result };
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ProviderUsage, ProviderUsageBalance, ProviderUsageWindow } from "../../server/messages.js";
|
|
3
|
+
import type { ProviderApiFetch } from "./provider.js";
|
|
4
|
+
export declare const ApiNumberSchema: z.ZodCoercedNumber<unknown>;
|
|
5
|
+
export declare const ApiNullableNumberSchema: z.ZodPreprocess<z.ZodNullable<z.ZodCoercedNumber<unknown>>>;
|
|
6
|
+
export declare const ApiOptionalStringSchema: z.ZodPreprocess<z.ZodOptional<z.ZodCoercedString<unknown>>>;
|
|
7
|
+
export declare function fetchProviderApi(fetchApi: ProviderApiFetch, input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
8
|
+
export declare function unavailableUsage(provider: {
|
|
9
|
+
providerId: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
error?: string | null;
|
|
12
|
+
}): ProviderUsage;
|
|
13
|
+
export declare function windowFromUsedPct(input: {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
utilizationPct: number | null | undefined;
|
|
17
|
+
resetsAt?: string | null;
|
|
18
|
+
tone?: ProviderUsageWindow["tone"];
|
|
19
|
+
}): ProviderUsageWindow;
|
|
20
|
+
export declare function balanceToneFromRemaining(remaining: number | null | undefined): ProviderUsageBalance["tone"];
|
|
21
|
+
export declare function toIsoStringOrNull(timestampMs: number): string | null;
|
|
22
|
+
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const PROVIDER_HTTP_TIMEOUT_MS = 15000;
|
|
3
|
+
export const ApiNumberSchema = z.coerce.number().finite();
|
|
4
|
+
export const ApiNullableNumberSchema = z.preprocess((value) => (value == null ? null : value), ApiNumberSchema.nullable());
|
|
5
|
+
export const ApiOptionalStringSchema = z.preprocess((value) => (value == null ? undefined : value), z.coerce.string().optional());
|
|
6
|
+
export function fetchProviderApi(fetchApi, input, init = {}) {
|
|
7
|
+
return fetchApi(input, {
|
|
8
|
+
...init,
|
|
9
|
+
signal: init.signal ?? AbortSignal.timeout(PROVIDER_HTTP_TIMEOUT_MS),
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function unavailableUsage(provider) {
|
|
13
|
+
return {
|
|
14
|
+
providerId: provider.providerId,
|
|
15
|
+
displayName: provider.displayName,
|
|
16
|
+
status: provider.error ? "error" : "unavailable",
|
|
17
|
+
planLabel: null,
|
|
18
|
+
windows: [],
|
|
19
|
+
balances: [],
|
|
20
|
+
details: [],
|
|
21
|
+
error: provider.error ?? null,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function windowFromUsedPct(input) {
|
|
25
|
+
const usedPct = typeof input.utilizationPct === "number" ? input.utilizationPct : null;
|
|
26
|
+
const window = {
|
|
27
|
+
id: input.id,
|
|
28
|
+
label: input.label,
|
|
29
|
+
usedPct,
|
|
30
|
+
remainingPct: usedPct === null ? null : Math.max(0, 100 - usedPct),
|
|
31
|
+
resetsAt: input.resetsAt ?? null,
|
|
32
|
+
};
|
|
33
|
+
if (input.tone) {
|
|
34
|
+
window.tone = input.tone;
|
|
35
|
+
}
|
|
36
|
+
return window;
|
|
37
|
+
}
|
|
38
|
+
export function balanceToneFromRemaining(remaining) {
|
|
39
|
+
if (typeof remaining !== "number")
|
|
40
|
+
return "default";
|
|
41
|
+
if (remaining <= 0)
|
|
42
|
+
return "danger";
|
|
43
|
+
return "ok";
|
|
44
|
+
}
|
|
45
|
+
export function toIsoStringOrNull(timestampMs) {
|
|
46
|
+
const date = new Date(timestampMs);
|
|
47
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : null;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=usage.js.map
|