pi-spark 0.19.0 → 0.19.2
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/README.md +1 -1
- package/package.json +11 -10
- package/src/features/credits/manager.ts +1 -1
- package/src/features/credits/providers/deepseek.ts +1 -1
- package/src/features/credits/providers/fireworks.ts +1 -1
- package/src/features/credits/providers/kimi-code.ts +1 -1
- package/src/features/credits/providers/moonshot.ts +1 -1
- package/src/features/credits/providers/openai-codex.ts +23 -15
- package/src/features/credits/providers/openrouter.ts +1 -1
- package/src/features/credits/providers/vercel-ai-gateway.ts +1 -1
- package/src/features/credits/types.ts +1 -1
- package/src/features/recap/idle.ts +5 -16
package/README.md
CHANGED
|
@@ -155,7 +155,7 @@ All fields are optional. If the recap model configuration is incomplete, pi-spar
|
|
|
155
155
|
|
|
156
156
|
| Field | Value | Description |
|
|
157
157
|
| --- | --- | --- |
|
|
158
|
-
| `idle` | number (ms) or duration string | How long the session must stay idle before a recap is generated. Accepts a millisecond number or a [
|
|
158
|
+
| `idle` | number (ms) or duration string | How long the session must stay idle before a recap is generated. Accepts a millisecond number or a [parse-duration](https://github.com/jkroso/parse-duration) string (e.g., `"5m"`); minimum 5000 ms, defaults to 5 minutes. |
|
|
159
159
|
| `provider` | string | Provider ID for the recap model. |
|
|
160
160
|
| `model` | string | Model ID for the recap model. |
|
|
161
161
|
| `thinkingLevel` | `ModelThinkingLevel` | Thinking level for the recap model. Defaults to `off`. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-spark",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "Pi package that polishes your daily experience.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-coding-agent",
|
|
@@ -33,21 +33,22 @@
|
|
|
33
33
|
"@grpc/grpc-js": "^1.14.4",
|
|
34
34
|
"@grpc/proto-loader": "^0.8.1",
|
|
35
35
|
"defu": "^6.1.7",
|
|
36
|
-
"
|
|
36
|
+
"parse-duration": "^2.1.8",
|
|
37
|
+
"pretty-ms": "^9.3.0",
|
|
37
38
|
"zod": "^4.4.3"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@earendil-works/pi-agent-core": ">=0.80.
|
|
41
|
-
"@earendil-works/pi-ai": ">=0.80.
|
|
42
|
-
"@earendil-works/pi-coding-agent": ">=0.80.
|
|
43
|
-
"@earendil-works/pi-tui": ">=0.80.
|
|
41
|
+
"@earendil-works/pi-agent-core": ">=0.80.8",
|
|
42
|
+
"@earendil-works/pi-ai": ">=0.80.8",
|
|
43
|
+
"@earendil-works/pi-coding-agent": ">=0.80.8",
|
|
44
|
+
"@earendil-works/pi-tui": ">=0.80.8",
|
|
44
45
|
"typebox": "*"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@earendil-works/pi-agent-core": ">=0.80.
|
|
48
|
-
"@earendil-works/pi-ai": ">=0.80.
|
|
49
|
-
"@earendil-works/pi-coding-agent": ">=0.80.
|
|
50
|
-
"@earendil-works/pi-tui": ">=0.80.
|
|
48
|
+
"@earendil-works/pi-agent-core": ">=0.80.8",
|
|
49
|
+
"@earendil-works/pi-ai": ">=0.80.8",
|
|
50
|
+
"@earendil-works/pi-coding-agent": ">=0.80.8",
|
|
51
|
+
"@earendil-works/pi-tui": ">=0.80.8",
|
|
51
52
|
"@types/node": "*",
|
|
52
53
|
"typebox": "*",
|
|
53
54
|
"typescript": "^7.0.2"
|
|
@@ -51,7 +51,7 @@ export class CreditsManager {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const signals = AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal]);
|
|
54
|
-
const credits = await provider.fetch(
|
|
54
|
+
const credits = await provider.fetch(apiKey, signals);
|
|
55
55
|
|
|
56
56
|
// The active model may have changed while the request was in flight.
|
|
57
57
|
if (ctx.model?.provider !== provider.id) return;
|
|
@@ -18,7 +18,7 @@ export const deepseekProvider: CreditsProvider = {
|
|
|
18
18
|
id: PROVIDER,
|
|
19
19
|
label: "DeepSeek",
|
|
20
20
|
|
|
21
|
-
async fetch(
|
|
21
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
22
22
|
const headers: Record<string, string> = {
|
|
23
23
|
Accept: "application/json",
|
|
24
24
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -108,7 +108,7 @@ export const fireworksProvider: CreditsProvider = {
|
|
|
108
108
|
id: PROVIDER,
|
|
109
109
|
label: "Fireworks",
|
|
110
110
|
|
|
111
|
-
async fetch(
|
|
111
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
112
112
|
const name = await resolveAccount(apiKey, signal);
|
|
113
113
|
const balance = await unary<Balance>("GetBalance", { name }, apiKey, signal);
|
|
114
114
|
|
|
@@ -42,7 +42,7 @@ export const kimiCodeProvider: CreditsProvider = {
|
|
|
42
42
|
id: PROVIDER,
|
|
43
43
|
label: "Kimi Code",
|
|
44
44
|
|
|
45
|
-
async fetch(
|
|
45
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
46
46
|
const headers: Record<string, string> = {
|
|
47
47
|
Accept: "application/json",
|
|
48
48
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -18,7 +18,7 @@ function createMoonshotProvider(id: string, host: string, currency: string): Cre
|
|
|
18
18
|
id,
|
|
19
19
|
label: "Moonshot",
|
|
20
20
|
|
|
21
|
-
async fetch(
|
|
21
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
22
22
|
const headers: Record<string, string> = {
|
|
23
23
|
Accept: "application/json",
|
|
24
24
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { uuidv7 } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { readStoredCredential } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import prettyMilliseconds from "pretty-ms";
|
|
2
4
|
|
|
3
5
|
import { confirmCodexReset, formatAvailableResets, showCodexResetLoader, showCodexResetSelector } from "./openai-codex-panel";
|
|
4
6
|
import { toNumber } from "../../../utils/format";
|
|
5
7
|
|
|
6
8
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
9
|
import type { CodexResetPanelData } from "./openai-codex-panel";
|
|
8
|
-
import type { Credits, CreditsProvider, RefreshCredits } from "../types";
|
|
10
|
+
import type { Credits, CreditsLane, CreditsProvider, RefreshCredits } from "../types";
|
|
9
11
|
|
|
10
12
|
const PROVIDER = "openai-codex";
|
|
11
13
|
const BASE_URL = "https://chatgpt.com/backend-api";
|
|
@@ -24,6 +26,7 @@ interface CodexUsageResponse {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
interface CodexRateWindow {
|
|
29
|
+
limit_window_seconds: number;
|
|
27
30
|
used_percent?: number | string;
|
|
28
31
|
}
|
|
29
32
|
|
|
@@ -50,7 +53,7 @@ async function runCodexReset(ctx: ExtensionContext, refresh: RefreshCredits): Pr
|
|
|
50
53
|
const apiKey = await ctx.modelRegistry.getApiKeyForProvider(PROVIDER);
|
|
51
54
|
if (!apiKey) return;
|
|
52
55
|
|
|
53
|
-
const headers = buildHeaders(
|
|
56
|
+
const headers = buildHeaders(apiKey);
|
|
54
57
|
|
|
55
58
|
const credit = await showCodexResetSelector(ctx, async (signal) => {
|
|
56
59
|
const requestSignal = AbortSignal.any([signal, AbortSignal.timeout(REQUEST_TIMEOUT_MS)]);
|
|
@@ -74,15 +77,15 @@ async function redeemReset(ctx: ExtensionContext, headers: Record<string, string
|
|
|
74
77
|
}).finally(() => refresh(ctx));
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
function buildHeaders(
|
|
80
|
+
function buildHeaders(apiKey: string): Record<string, string> {
|
|
78
81
|
const headers: Record<string, string> = {
|
|
79
82
|
Accept: "application/json",
|
|
80
83
|
Authorization: `Bearer ${apiKey}`,
|
|
81
84
|
};
|
|
82
85
|
|
|
83
|
-
const credential =
|
|
84
|
-
const accountId = credential?.type === "oauth" ?
|
|
85
|
-
if (accountId) headers["ChatGPT-Account-ID"] = accountId;
|
|
86
|
+
const credential = readStoredCredential(PROVIDER);
|
|
87
|
+
const accountId = credential?.type === "oauth" ? credential.accountId : undefined;
|
|
88
|
+
if (typeof accountId === "string" && accountId.length > 0) headers["ChatGPT-Account-ID"] = accountId;
|
|
86
89
|
|
|
87
90
|
return headers;
|
|
88
91
|
}
|
|
@@ -126,14 +129,20 @@ function toCredits(usage: CodexUsageResponse, suffix?: string): Credits {
|
|
|
126
129
|
return {
|
|
127
130
|
type: "windows",
|
|
128
131
|
unlimited: usage.credits?.unlimited === true,
|
|
129
|
-
lanes: [
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
],
|
|
132
|
+
lanes: [usage.rate_limit?.primary_window, usage.rate_limit?.secondary_window]
|
|
133
|
+
.filter((window): window is CodexRateWindow => window != null)
|
|
134
|
+
.map(toLane),
|
|
133
135
|
suffix,
|
|
134
136
|
};
|
|
135
137
|
}
|
|
136
138
|
|
|
139
|
+
function toLane(window: CodexRateWindow): CreditsLane {
|
|
140
|
+
return {
|
|
141
|
+
label: prettyMilliseconds(window.limit_window_seconds * 1_000, { compact: true }),
|
|
142
|
+
percent: parseUsedPercent(window),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
137
146
|
function parseUsedPercent(window?: CodexRateWindow | null): number | undefined {
|
|
138
147
|
const value = toNumber(window?.used_percent);
|
|
139
148
|
return value === undefined ? undefined : Math.min(100, Math.max(0, value));
|
|
@@ -143,8 +152,8 @@ export const openaiCodexProvider: CreditsProvider = {
|
|
|
143
152
|
id: PROVIDER,
|
|
144
153
|
label: "Codex",
|
|
145
154
|
|
|
146
|
-
async fetch(
|
|
147
|
-
const headers = buildHeaders(
|
|
155
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
156
|
+
const headers = buildHeaders(apiKey);
|
|
148
157
|
const usage = await fetchUsage(headers, signal);
|
|
149
158
|
const availableCount = usage.rate_limit_reset_credits?.available_count ?? 0;
|
|
150
159
|
const suffix = availableCount > 0 ? `(${formatAvailableResets(availableCount)})` : undefined;
|
|
@@ -152,9 +161,8 @@ export const openaiCodexProvider: CreditsProvider = {
|
|
|
152
161
|
return toCredits(usage, suffix);
|
|
153
162
|
},
|
|
154
163
|
|
|
155
|
-
register(pi,
|
|
156
|
-
|
|
157
|
-
if (credential?.type !== "oauth") return;
|
|
164
|
+
register(pi, _ctx, refresh): void {
|
|
165
|
+
if (readStoredCredential(PROVIDER)?.type !== "oauth") return;
|
|
158
166
|
|
|
159
167
|
pi.registerCommand("codex-reset", {
|
|
160
168
|
description: "Select and redeem a banked OpenAI Codex rate-limit reset",
|
|
@@ -16,7 +16,7 @@ export const openrouterProvider: CreditsProvider = {
|
|
|
16
16
|
id: PROVIDER,
|
|
17
17
|
label: "OpenRouter",
|
|
18
18
|
|
|
19
|
-
async fetch(
|
|
19
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
20
20
|
const headers: Record<string, string> = {
|
|
21
21
|
Accept: "application/json",
|
|
22
22
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -13,7 +13,7 @@ export const vercelAiGatewayProvider: CreditsProvider = {
|
|
|
13
13
|
id: PROVIDER,
|
|
14
14
|
label: "Vercel",
|
|
15
15
|
|
|
16
|
-
async fetch(
|
|
16
|
+
async fetch(apiKey, signal): Promise<Credits> {
|
|
17
17
|
const headers: Record<string, string> = {
|
|
18
18
|
Accept: "application/json",
|
|
19
19
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -23,6 +23,6 @@ export type RefreshCredits = (ctx: ExtensionContext) => Promise<void>;
|
|
|
23
23
|
export interface CreditsProvider {
|
|
24
24
|
readonly id: ProviderId;
|
|
25
25
|
readonly label: string;
|
|
26
|
-
fetch(
|
|
26
|
+
fetch(apiKey: string, signal: AbortSignal): Promise<Credits>;
|
|
27
27
|
register?(pi: ExtensionAPI, ctx: ExtensionContext, refresh: RefreshCredits): void;
|
|
28
28
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import parseDuration from "parse-duration";
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
|
|
4
|
-
import type { StringValue } from "ms";
|
|
5
|
-
|
|
6
4
|
const MIN_IDLE_MS = 5_000;
|
|
7
5
|
|
|
8
6
|
/** Accept a millisecond number or a human-readable duration (e.g., "3m"), normalized to milliseconds. */
|
|
@@ -11,20 +9,11 @@ export const idleTimeoutSchema = z
|
|
|
11
9
|
.transform((value, ctx) => {
|
|
12
10
|
if (typeof value === "number") return value;
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
parsed = ms(value as StringValue);
|
|
17
|
-
} catch (error) {
|
|
18
|
-
ctx.addIssue({ code: "custom", message: error instanceof Error ? error.message : String(error) });
|
|
19
|
-
return z.NEVER;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (Number.isNaN(parsed)) {
|
|
23
|
-
ctx.addIssue({ code: "custom", message: `Value is not a valid duration. value=${JSON.stringify(value)}` });
|
|
24
|
-
return z.NEVER;
|
|
25
|
-
}
|
|
12
|
+
const parsed = parseDuration(value);
|
|
13
|
+
if (parsed !== null) return parsed;
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
ctx.addIssue({ code: "custom", message: `Value is not a valid duration. value=${JSON.stringify(value)}` });
|
|
16
|
+
return z.NEVER;
|
|
28
17
|
})
|
|
29
18
|
.pipe(z.number().min(MIN_IDLE_MS));
|
|
30
19
|
|