pi-spark 0.19.0 → 0.19.1

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 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 [vercel/ms](https://github.com/vercel/ms) string (e.g., `"5m"`); minimum 5000 ms, defaults to 5 minutes. |
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.0",
3
+ "version": "0.19.1",
4
4
  "description": "Pi package that polishes your daily experience.",
5
5
  "keywords": [
6
6
  "pi-coding-agent",
@@ -33,7 +33,8 @@
33
33
  "@grpc/grpc-js": "^1.14.4",
34
34
  "@grpc/proto-loader": "^0.8.1",
35
35
  "defu": "^6.1.7",
36
- "ms": "4.0.0-nightly.202508271359",
36
+ "parse-duration": "^2.1.8",
37
+ "pretty-ms": "^9.3.0",
37
38
  "zod": "^4.4.3"
38
39
  },
39
40
  "peerDependencies": {
@@ -1,11 +1,12 @@
1
1
  import { uuidv7 } from "@earendil-works/pi-agent-core";
2
+ import prettyMilliseconds from "pretty-ms";
2
3
 
3
4
  import { confirmCodexReset, formatAvailableResets, showCodexResetLoader, showCodexResetSelector } from "./openai-codex-panel";
4
5
  import { toNumber } from "../../../utils/format";
5
6
 
6
7
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
7
8
  import type { CodexResetPanelData } from "./openai-codex-panel";
8
- import type { Credits, CreditsProvider, RefreshCredits } from "../types";
9
+ import type { Credits, CreditsLane, CreditsProvider, RefreshCredits } from "../types";
9
10
 
10
11
  const PROVIDER = "openai-codex";
11
12
  const BASE_URL = "https://chatgpt.com/backend-api";
@@ -24,6 +25,7 @@ interface CodexUsageResponse {
24
25
  }
25
26
 
26
27
  interface CodexRateWindow {
28
+ limit_window_seconds: number;
27
29
  used_percent?: number | string;
28
30
  }
29
31
 
@@ -126,14 +128,20 @@ function toCredits(usage: CodexUsageResponse, suffix?: string): Credits {
126
128
  return {
127
129
  type: "windows",
128
130
  unlimited: usage.credits?.unlimited === true,
129
- lanes: [
130
- { label: "5h", percent: parseUsedPercent(usage.rate_limit?.primary_window) },
131
- { label: "7d", percent: parseUsedPercent(usage.rate_limit?.secondary_window) },
132
- ],
131
+ lanes: [usage.rate_limit?.primary_window, usage.rate_limit?.secondary_window]
132
+ .filter((window): window is CodexRateWindow => window != null)
133
+ .map(toLane),
133
134
  suffix,
134
135
  };
135
136
  }
136
137
 
138
+ function toLane(window: CodexRateWindow): CreditsLane {
139
+ return {
140
+ label: prettyMilliseconds(window.limit_window_seconds * 1_000, { compact: true }),
141
+ percent: parseUsedPercent(window),
142
+ };
143
+ }
144
+
137
145
  function parseUsedPercent(window?: CodexRateWindow | null): number | undefined {
138
146
  const value = toNumber(window?.used_percent);
139
147
  return value === undefined ? undefined : Math.min(100, Math.max(0, value));
@@ -1,8 +1,6 @@
1
- import { ms } from "ms";
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
- let parsed: number;
15
- try {
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
- return parsed;
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