@juanibiapina/pi-powerbar 0.11.1 → 0.12.0
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/node_modules/@juanibiapina/pi-usage/.github/workflows/dependabot-auto-merge.yml +129 -0
- package/node_modules/@juanibiapina/pi-usage/.github/workflows/publish.yml +25 -0
- package/node_modules/@juanibiapina/pi-usage/.local/share/pi/socket.info.json +8 -0
- package/node_modules/@juanibiapina/pi-usage/AGENTS.md +10 -0
- package/node_modules/@juanibiapina/pi-usage/CHANGELOG.md +10 -0
- package/node_modules/@juanibiapina/pi-usage/README.md +39 -0
- package/node_modules/@juanibiapina/pi-usage/biome.json +27 -0
- package/node_modules/@juanibiapina/pi-usage/docs/releases.md +40 -0
- package/node_modules/@juanibiapina/pi-usage/index.ts +150 -0
- package/node_modules/@juanibiapina/pi-usage/package.json +37 -0
- package/node_modules/@juanibiapina/pi-usage/src/cache.ts +240 -0
- package/node_modules/@juanibiapina/pi-usage/src/dependencies.ts +34 -0
- package/node_modules/@juanibiapina/pi-usage/src/detection.ts +61 -0
- package/node_modules/@juanibiapina/pi-usage/src/errors.ts +38 -0
- package/node_modules/@juanibiapina/pi-usage/src/provider.ts +34 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/anthropic.ts +146 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/antigravity.ts +207 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/codex.ts +137 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/copilot.ts +167 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/gemini.ts +119 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/kiro.ts +89 -0
- package/node_modules/@juanibiapina/pi-usage/src/providers/zai.ts +116 -0
- package/node_modules/@juanibiapina/pi-usage/src/registry.ts +32 -0
- package/node_modules/@juanibiapina/pi-usage/src/types.ts +72 -0
- package/node_modules/@juanibiapina/pi-usage/src/utils.ts +70 -0
- package/node_modules/@juanibiapina/pi-usage/test/detection.test.ts +76 -0
- package/node_modules/@juanibiapina/pi-usage/test/extension.test.ts +138 -0
- package/node_modules/@juanibiapina/pi-usage/tsconfig.json +17 -0
- package/package.json +8 -4
- package/src/powerbar-sub/index.ts +35 -18
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
name: Dependabot auto-merge
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
pull_request_target:
|
|
6
|
+
types:
|
|
7
|
+
- opened
|
|
8
|
+
- reopened
|
|
9
|
+
- synchronize
|
|
10
|
+
- ready_for_review
|
|
11
|
+
check_run:
|
|
12
|
+
types:
|
|
13
|
+
- completed
|
|
14
|
+
check_suite:
|
|
15
|
+
types:
|
|
16
|
+
- completed
|
|
17
|
+
status:
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: write
|
|
21
|
+
pull-requests: write
|
|
22
|
+
checks: read
|
|
23
|
+
statuses: read
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
merge:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- name: Merge Dependabot PRs when checks pass
|
|
30
|
+
uses: actions/github-script@v9
|
|
31
|
+
with:
|
|
32
|
+
github-token: ${{ github.token }}
|
|
33
|
+
script: |
|
|
34
|
+
const owner = context.repo.owner
|
|
35
|
+
const repo = context.repo.repo
|
|
36
|
+
const okConclusions = new Set(['success', 'neutral', 'skipped'])
|
|
37
|
+
|
|
38
|
+
async function prsForSha(ref) {
|
|
39
|
+
const res = await github.request('GET /repos/{owner}/{repo}/commits/{ref}/pulls', {
|
|
40
|
+
owner,
|
|
41
|
+
repo,
|
|
42
|
+
ref,
|
|
43
|
+
mediaType: { previews: ['groot'] },
|
|
44
|
+
})
|
|
45
|
+
return res.data
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function listCandidatePRs() {
|
|
49
|
+
if (context.eventName === 'workflow_dispatch') {
|
|
50
|
+
const res = await github.rest.pulls.list({ owner, repo, state: 'open', per_page: 100 })
|
|
51
|
+
return res.data.filter((pr) => pr.user?.login === 'dependabot[bot]')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (context.eventName === 'pull_request_target') {
|
|
55
|
+
const pr = context.payload.pull_request
|
|
56
|
+
if (pr?.state === 'open' && pr.user?.login === 'dependabot[bot]') {
|
|
57
|
+
return [pr]
|
|
58
|
+
}
|
|
59
|
+
return []
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const ref = context.payload.check_run?.head_sha || context.payload.check_suite?.head_sha || context.payload.sha
|
|
63
|
+
if (!ref) {
|
|
64
|
+
return []
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const prs = await prsForSha(ref)
|
|
68
|
+
return prs.filter((pr) => pr.state === 'open' && pr.user?.login === 'dependabot[bot]')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function checksPassed(ref) {
|
|
72
|
+
const [statusRes, checkRunsRes] = await Promise.all([
|
|
73
|
+
github.rest.repos.getCombinedStatusForRef({ owner, repo, ref }),
|
|
74
|
+
github.rest.checks.listForRef({ owner, repo, ref, per_page: 100 }),
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
const statuses = statusRes.data.statuses || []
|
|
78
|
+
const checkRuns = checkRunsRes.data.check_runs || []
|
|
79
|
+
|
|
80
|
+
if (statuses.some((status) => status.state !== 'success')) {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const run of checkRuns) {
|
|
85
|
+
if (run.status !== 'completed') {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
if (!okConclusions.has(run.conclusion)) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const seen = new Set()
|
|
97
|
+
const candidates = await listCandidatePRs()
|
|
98
|
+
|
|
99
|
+
for (const candidate of candidates) {
|
|
100
|
+
if (seen.has(candidate.number)) {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
seen.add(candidate.number)
|
|
104
|
+
|
|
105
|
+
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: candidate.number })
|
|
106
|
+
|
|
107
|
+
if (pr.state !== 'open' || pr.user?.login !== 'dependabot[bot]' || pr.draft) {
|
|
108
|
+
core.info(`Skipping #${pr.number}`)
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const ready = await checksPassed(pr.head.sha)
|
|
113
|
+
if (!ready) {
|
|
114
|
+
core.info(`Checks not green for #${pr.number}`)
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
await github.rest.pulls.merge({
|
|
120
|
+
owner,
|
|
121
|
+
repo,
|
|
122
|
+
pull_number: pr.number,
|
|
123
|
+
merge_method: 'squash',
|
|
124
|
+
})
|
|
125
|
+
core.info(`Merged #${pr.number}`)
|
|
126
|
+
} catch (error) {
|
|
127
|
+
core.warning(`Could not merge #${pr.number}: ${error.message}`)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v6
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v6
|
|
19
|
+
with:
|
|
20
|
+
node-version: '24'
|
|
21
|
+
registry-url: 'https://registry.npmjs.org'
|
|
22
|
+
|
|
23
|
+
- run: npm ci
|
|
24
|
+
|
|
25
|
+
- run: npm publish --access public
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"protocolVersion": 1,
|
|
3
|
+
"cwd": "/Users/juan.ibiapina/workspace/juanibiapina/pi-usage",
|
|
4
|
+
"pid": 25431,
|
|
5
|
+
"socketPath": "/Users/juan.ibiapina/workspace/juanibiapina/pi-usage/.local/share/pi/socket",
|
|
6
|
+
"startedAt": "2026-05-20T16:47:45.523Z",
|
|
7
|
+
"sessionFile": "/Users/juan.ibiapina/.pi/agent/sessions/--Users-juan.ibiapina-workspace-juanibiapina-pi-usage--/2026-05-20T16-47-43-822Z_019e4649-36ce-715f-ab4f-af547f13287f.jsonl"
|
|
8
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-05-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Initial release: simplified fork of [@marckrenn/pi-sub-core](https://github.com/marckrenn/pi-sub-core)
|
|
8
|
+
- Fetches Anthropic subscription usage data and renders it in the Pi status bar
|
|
9
|
+
- Auto-detects LLM provider from model metadata
|
|
10
|
+
- Configurable refresh interval via `PI_USAGE_REFRESH_MINUTES` environment variable
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# pi-usage
|
|
2
|
+
|
|
3
|
+
Pi extension that fetches subscription usage for all supported providers (Anthropic, Copilot, Gemini, Antigravity, Codex, Kiro, z.ai).
|
|
4
|
+
|
|
5
|
+
Simplified fork of the excellent [@marckrenn/pi-sub-core](https://github.com/marckrenn/pi-sub). Keeps all providers, applies two bug fixes, drops features we don't need.
|
|
6
|
+
|
|
7
|
+
## Bug fixes
|
|
8
|
+
|
|
9
|
+
### Bedrock false positive detection
|
|
10
|
+
|
|
11
|
+
pi-sub-core's `detectProviderFromModel` falls back to matching model tokens (e.g. `"claude"`) when provider tokens don't match. This causes AWS Bedrock models (which run Claude but are billed separately) to be misidentified as Anthropic subscription usage. pi-usage only falls back to model tokens when no explicit provider is set.
|
|
12
|
+
|
|
13
|
+
### Aggressive refresh causing 429 flicker
|
|
14
|
+
|
|
15
|
+
pi-sub-core uses `force: true` on `turn_end` and `tool_result`, bypassing the cache TTL and hammering the usage APIs. Under load (multiple pi instances), this triggers 429s that cause the usage display to flicker. pi-usage always respects the cache TTL. See [marckrenn/pi-sub#58](https://github.com/marckrenn/pi-sub/issues/58).
|
|
16
|
+
|
|
17
|
+
## Simplifications vs pi-sub-core
|
|
18
|
+
|
|
19
|
+
- No status page fetching
|
|
20
|
+
- No settings UI or settings persistence
|
|
21
|
+
- No tool registration
|
|
22
|
+
- No `update-all` event (only `update-current`)
|
|
23
|
+
- No provider cycle command
|
|
24
|
+
- Self-contained types — no dependency on `@marckrenn/pi-sub-shared`
|
|
25
|
+
|
|
26
|
+
## Events
|
|
27
|
+
|
|
28
|
+
| Event | Payload | Description |
|
|
29
|
+
|-------|---------|-------------|
|
|
30
|
+
| `usage-core:ready` | `{ state: UsageCoreState }` | Emitted once on session start |
|
|
31
|
+
| `usage-core:update-current` | `{ state: UsageCoreState }` | Emitted on usage changes |
|
|
32
|
+
|
|
33
|
+
`UsageCoreState` has an optional `provider` name and optional `usage` snapshot with rate windows. When `provider` is undefined, no known subscription provider was detected for the current model.
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
pi install npm:@juanibiapina/pi-usage
|
|
39
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.4.15/schema.json",
|
|
3
|
+
"linter": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"rules": {
|
|
6
|
+
"recommended": true,
|
|
7
|
+
"style": {
|
|
8
|
+
"noNonNullAssertion": "off",
|
|
9
|
+
"useConst": "error",
|
|
10
|
+
"useNodejsImportProtocol": "off"
|
|
11
|
+
},
|
|
12
|
+
"suspicious": {
|
|
13
|
+
"noExplicitAny": "off"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"formatter": {
|
|
18
|
+
"enabled": true,
|
|
19
|
+
"formatWithErrors": false,
|
|
20
|
+
"indentStyle": "tab",
|
|
21
|
+
"indentWidth": 3,
|
|
22
|
+
"lineWidth": 120
|
|
23
|
+
},
|
|
24
|
+
"files": {
|
|
25
|
+
"includes": ["index.ts", "src/**/*.ts", "test/**/*.ts", "!**/node_modules/**/*"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
## Creating a Release
|
|
4
|
+
|
|
5
|
+
Releases are automated via GitHub Actions and triggered by git tags. The workflow uses npm OIDC trusted publishing (no tokens required).
|
|
6
|
+
|
|
7
|
+
1. Update `CHANGELOG.md`: move `[Unreleased]` entries to new version section with date
|
|
8
|
+
2. Bump version in `package.json`:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm version patch # or minor, or major
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
3. Push with tags:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
git push origin main --tags
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The workflow automatically:
|
|
21
|
+
- Installs dependencies (`npm ci`)
|
|
22
|
+
- Publishes TypeScript source to npm (`npm publish --access public`)
|
|
23
|
+
|
|
24
|
+
After pushing the tag, the coding agent should:
|
|
25
|
+
1. Wait for the GitHub Actions workflow to complete: `gh run watch`
|
|
26
|
+
2. Verify the publish: `npm view @juanibiapina/pi-usage`
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
OIDC trusted publishing must be configured on [npmjs.com](https://www.npmjs.com):
|
|
31
|
+
- Package settings → Trusted Publishers
|
|
32
|
+
- Repository: `juanibiapina/pi-usage`
|
|
33
|
+
- Workflow: `publish.yml`
|
|
34
|
+
|
|
35
|
+
## Version Format
|
|
36
|
+
|
|
37
|
+
Follow [semantic versioning](https://semver.org/):
|
|
38
|
+
|
|
39
|
+
- **Production**: `v1.2.3`
|
|
40
|
+
- **Pre-release**: `v1.0.0-beta.1`, `v1.0.0-rc.1`, `v1.0.0-alpha.1`
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-usage — Subscription usage extension for all providers.
|
|
3
|
+
*
|
|
4
|
+
* Simplified fork of @marckrenn/pi-sub-core (https://github.com/marckrenn/pi-sub).
|
|
5
|
+
* Supports all providers (anthropic, copilot, gemini, antigravity, codex, kiro, zai)
|
|
6
|
+
* with two bug fixes:
|
|
7
|
+
*
|
|
8
|
+
* 1. Bedrock false positive: detection no longer falls back to model tokens when
|
|
9
|
+
* the provider field is explicitly set and doesn't match any known provider.
|
|
10
|
+
*
|
|
11
|
+
* 2. Aggressive refresh on turn_end/tool_result: always respects cache TTL instead
|
|
12
|
+
* of bypassing it with force:true (see https://github.com/marckrenn/pi-sub/issues/58).
|
|
13
|
+
*
|
|
14
|
+
* Emits:
|
|
15
|
+
* - "usage-core:ready" → { state: UsageCoreState }
|
|
16
|
+
* - "usage-core:update-current" → { state: UsageCoreState }
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
20
|
+
import { fetchWithCache, getGoodUsage, watchCache } from "./src/cache.js";
|
|
21
|
+
import { createDefaultDependencies } from "./src/dependencies.js";
|
|
22
|
+
import { detectProvider } from "./src/detection.js";
|
|
23
|
+
import { createProvider, hasCredentials } from "./src/registry.js";
|
|
24
|
+
import type { Dependencies, ProviderName, UsageCoreState, UsageSnapshot } from "./src/types.js";
|
|
25
|
+
|
|
26
|
+
const REFRESH_INTERVAL_MS = 60_000;
|
|
27
|
+
|
|
28
|
+
type GlobalGuard = { active: boolean };
|
|
29
|
+
const global = globalThis as typeof globalThis & { __piUsage?: GlobalGuard };
|
|
30
|
+
|
|
31
|
+
export default function createExtension(pi: ExtensionAPI, deps?: Dependencies): void {
|
|
32
|
+
const resolvedDeps = deps ?? createDefaultDependencies();
|
|
33
|
+
// Prevent double-init when bundled alongside other extensions.
|
|
34
|
+
// Skip the guard when deps are explicitly provided (test mode).
|
|
35
|
+
if (!deps && global.__piUsage?.active) return;
|
|
36
|
+
if (!deps) global.__piUsage = { active: true };
|
|
37
|
+
|
|
38
|
+
let lastContext: ExtensionContext | undefined;
|
|
39
|
+
let lastState: UsageCoreState = {};
|
|
40
|
+
let lastSnapshot = "";
|
|
41
|
+
let currentProvider: ProviderName | undefined;
|
|
42
|
+
let stopCacheWatch: (() => void) | undefined;
|
|
43
|
+
|
|
44
|
+
// --- Emit helpers ---
|
|
45
|
+
|
|
46
|
+
function emitState(state: UsageCoreState): void {
|
|
47
|
+
const json = JSON.stringify(state);
|
|
48
|
+
if (json === lastSnapshot) return;
|
|
49
|
+
lastSnapshot = json;
|
|
50
|
+
lastState = state;
|
|
51
|
+
pi.events.emit("usage-core:update-current", { state });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function setupCacheWatch(provider: ProviderName): void {
|
|
55
|
+
stopCacheWatch?.();
|
|
56
|
+
stopCacheWatch = watchCache(provider, (usage: UsageSnapshot) => {
|
|
57
|
+
if (currentProvider === provider) {
|
|
58
|
+
emitState({ provider, usage });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// --- Refresh ---
|
|
64
|
+
|
|
65
|
+
async function refresh(ctx: ExtensionContext, force = false): Promise<void> {
|
|
66
|
+
lastContext = ctx;
|
|
67
|
+
|
|
68
|
+
const detected = detectProvider(ctx.model);
|
|
69
|
+
if (!detected) {
|
|
70
|
+
currentProvider = undefined;
|
|
71
|
+
emitState({});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Provider changed — reset.
|
|
76
|
+
if (detected !== currentProvider) {
|
|
77
|
+
currentProvider = detected;
|
|
78
|
+
setupCacheWatch(detected);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!hasCredentials(detected, resolvedDeps)) {
|
|
82
|
+
emitState({ provider: detected });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check cache first (unless forced).
|
|
87
|
+
if (!force) {
|
|
88
|
+
const cached = getGoodUsage(detected, REFRESH_INTERVAL_MS);
|
|
89
|
+
if (cached) {
|
|
90
|
+
emitState({ provider: detected, usage: cached });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const providerInstance = createProvider(detected);
|
|
96
|
+
const usage = await fetchWithCache(detected, REFRESH_INTERVAL_MS, () =>
|
|
97
|
+
providerInstance.fetchUsage(resolvedDeps),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// Only emit when we got good data. On errors (429 etc.),
|
|
101
|
+
// fetchWithCache writes a backoff file so all instances wait.
|
|
102
|
+
// UI keeps showing last good state.
|
|
103
|
+
if (usage) {
|
|
104
|
+
emitState({ provider: detected, usage });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- Periodic refresh ---
|
|
109
|
+
|
|
110
|
+
const refreshTimer = setInterval(() => {
|
|
111
|
+
if (!lastContext || !currentProvider) return;
|
|
112
|
+
void refresh(lastContext);
|
|
113
|
+
}, REFRESH_INTERVAL_MS);
|
|
114
|
+
refreshTimer.unref?.();
|
|
115
|
+
|
|
116
|
+
// --- Lifecycle ---
|
|
117
|
+
|
|
118
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
119
|
+
lastContext = ctx;
|
|
120
|
+
await refresh(ctx);
|
|
121
|
+
pi.events.emit("usage-core:ready", { state: lastState });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
pi.on("model_select" as any, async (_event: unknown, ctx: ExtensionContext) => {
|
|
125
|
+
// Model changed — force refresh.
|
|
126
|
+
await refresh(ctx, true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
pi.on("turn_start", async (_event, ctx) => {
|
|
130
|
+
// Respect TTL — emit cached, don't force.
|
|
131
|
+
lastContext = ctx;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
pi.on("turn_end", async (_event, ctx) => {
|
|
135
|
+
// Respect TTL — this is the fix for pi-sub#58.
|
|
136
|
+
lastContext = ctx;
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
pi.on("session_switch" as any, async (_event: unknown, ctx: ExtensionContext) => {
|
|
140
|
+
currentProvider = undefined;
|
|
141
|
+
await refresh(ctx, true);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
pi.on("session_shutdown", async () => {
|
|
145
|
+
clearInterval(refreshTimer);
|
|
146
|
+
stopCacheWatch?.();
|
|
147
|
+
lastContext = undefined;
|
|
148
|
+
if (!deps) global.__piUsage = undefined;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@juanibiapina/pi-usage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pi extension that fetches Anthropic subscription usage. Simplified fork of @marckrenn/pi-sub-core.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package"
|
|
8
|
+
],
|
|
9
|
+
"pi": {
|
|
10
|
+
"extensions": [
|
|
11
|
+
"./index.ts"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node --test --import tsx test/*.test.ts",
|
|
16
|
+
"check": "biome check --write --error-on-warnings . && tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"author": "Juan Ibiapina",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/juanibiapina/pi-usage"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20.0.0"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@earendil-works/pi-coding-agent": "*"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@biomejs/biome": "2.4.15",
|
|
32
|
+
"@earendil-works/pi-coding-agent": "^0.75.3",
|
|
33
|
+
"@types/node": "^25.9.1",
|
|
34
|
+
"tsx": "^4.22.3",
|
|
35
|
+
"typescript": "^6.0.3"
|
|
36
|
+
}
|
|
37
|
+
}
|