@tduyng/pi-cost 0.0.1 → 0.0.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/package.json +1 -1
- package/src/cost/index.ts +20 -12
- package/src/statusline/index.ts +4 -3
- package/src/timestamps/logic.ts +1 -3
package/package.json
CHANGED
package/src/cost/index.ts
CHANGED
|
@@ -86,9 +86,12 @@ function errorText(cause: unknown): string {
|
|
|
86
86
|
return cause instanceof Error ? cause.message : String(cause)
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export async function claudeToken(
|
|
89
|
+
export async function claudeToken(
|
|
90
|
+
pi: ExtensionAPI,
|
|
91
|
+
platform: NodeJS.Platform = process.platform
|
|
92
|
+
): Promise<string | undefined> {
|
|
90
93
|
let raw: string | undefined
|
|
91
|
-
if (
|
|
94
|
+
if (platform === 'darwin') {
|
|
92
95
|
const result = await pi.exec('security', ['find-generic-password', '-s', 'Claude Code-credentials', '-w'], {
|
|
93
96
|
cwd: os.homedir(),
|
|
94
97
|
timeout: KEYCHAIN_TIMEOUT_MS
|
|
@@ -166,11 +169,18 @@ function modelRates(ctx: ExtensionCommandContext): ModelRates | undefined {
|
|
|
166
169
|
: undefined
|
|
167
170
|
}
|
|
168
171
|
|
|
169
|
-
function sessionSpend(ctx: ExtensionCommandContext): SessionSpend {
|
|
172
|
+
function sessionSpend(ctx: ExtensionCommandContext, overheadRecords: ReturnType<typeof readOverhead>): SessionSpend {
|
|
170
173
|
const turns: SessionTurn[] = []
|
|
171
174
|
let costUsd = 0
|
|
172
175
|
let collapsed = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
173
|
-
const
|
|
176
|
+
const currentRates = modelRates(ctx)
|
|
177
|
+
const ratesFor = (provider: string, model: string): ModelRates | undefined => {
|
|
178
|
+
const found = ctx.modelRegistry.find(provider, model)
|
|
179
|
+
const cost = found?.cost
|
|
180
|
+
return cost
|
|
181
|
+
? { input: cost.input, output: cost.output, cacheRead: cost.cacheRead, cacheWrite: cost.cacheWrite }
|
|
182
|
+
: undefined
|
|
183
|
+
}
|
|
174
184
|
for (const entry of ctx.sessionManager.getBranch()) {
|
|
175
185
|
if (entry.type === 'branch_summary') {
|
|
176
186
|
if (entry.usage) {
|
|
@@ -181,7 +191,7 @@ function sessionSpend(ctx: ExtensionCommandContext): SessionSpend {
|
|
|
181
191
|
cacheRead: collapsed.cacheRead + usage.cacheRead,
|
|
182
192
|
cacheWrite: collapsed.cacheWrite + usage.cacheWrite
|
|
183
193
|
}
|
|
184
|
-
costUsd = addTurnCost(costUsd, usage,
|
|
194
|
+
costUsd = addTurnCost(costUsd, usage, currentRates)
|
|
185
195
|
}
|
|
186
196
|
continue
|
|
187
197
|
}
|
|
@@ -191,7 +201,7 @@ function sessionSpend(ctx: ExtensionCommandContext): SessionSpend {
|
|
|
191
201
|
continue
|
|
192
202
|
}
|
|
193
203
|
const usage = entry.message.usage
|
|
194
|
-
costUsd = addTurnCost(costUsd, usage,
|
|
204
|
+
costUsd = addTurnCost(costUsd, usage, ratesFor(entry.message.provider, entry.message.model) ?? currentRates)
|
|
195
205
|
turns.push({ usage, timestamp: entry.timestamp })
|
|
196
206
|
}
|
|
197
207
|
const totals = sessionTotals(turns)
|
|
@@ -199,17 +209,14 @@ function sessionSpend(ctx: ExtensionCommandContext): SessionSpend {
|
|
|
199
209
|
totals.output += collapsed.output
|
|
200
210
|
totals.cacheRead += collapsed.cacheRead
|
|
201
211
|
totals.cacheWrite += collapsed.cacheWrite
|
|
202
|
-
|
|
203
|
-
return { costUsd, totals, overhead }
|
|
212
|
+
return { costUsd, totals, overhead: sumOverhead(overheadRecords) }
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
function sessionLines(session: SessionSpend, theme: Theme): string[] {
|
|
207
216
|
const label = (text: string) => theme.fg('dim', text.padEnd(LABEL_WIDTH))
|
|
208
217
|
const totals = session.totals
|
|
209
218
|
const hit = formatCacheHit(totals)
|
|
210
|
-
const extensions = [...session.overhead.byExtension]
|
|
211
|
-
.map(([name, spend]) => `${name} ${spend.calls}`)
|
|
212
|
-
.join(', ')
|
|
219
|
+
const extensions = [...session.overhead.byExtension].map(([name, spend]) => `${name} ${spend.calls}`).join(', ')
|
|
213
220
|
return [
|
|
214
221
|
` ${label('Cost')}${formatCostUsd(session.costUsd)}`,
|
|
215
222
|
...(session.overhead.calls === 0
|
|
@@ -370,6 +377,7 @@ export default function costExtension(pi: ExtensionAPI): void {
|
|
|
370
377
|
container.addChild(border)
|
|
371
378
|
|
|
372
379
|
const usage = ctx.getContextUsage()
|
|
380
|
+
const overheadRecords = readOverhead(getAgentDir(), ctx.sessionManager.getSessionId())
|
|
373
381
|
const header = [
|
|
374
382
|
ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined,
|
|
375
383
|
usage?.percent != null ? `context ${Math.round(usage.percent)}%` : undefined
|
|
@@ -387,7 +395,7 @@ export default function costExtension(pi: ExtensionAPI): void {
|
|
|
387
395
|
}
|
|
388
396
|
|
|
389
397
|
render = () => {
|
|
390
|
-
const session = sessionSpend(ctx)
|
|
398
|
+
const session = sessionSpend(ctx, overheadRecords)
|
|
391
399
|
lines = [
|
|
392
400
|
`${theme.fg('accent', theme.bold('Cost and limits'))} ${theme.fg('dim', header)}`,
|
|
393
401
|
'',
|
package/src/statusline/index.ts
CHANGED
|
@@ -57,6 +57,7 @@ export default function statuslineExtension(pi: ExtensionAPI): void {
|
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
pi.on('agent_settled', () => {
|
|
60
|
+
if (!enabled) return
|
|
60
61
|
void refreshGit()
|
|
61
62
|
})
|
|
62
63
|
|
|
@@ -73,7 +74,7 @@ export default function statuslineExtension(pi: ExtensionAPI): void {
|
|
|
73
74
|
|
|
74
75
|
async function refreshGit(): Promise<void> {
|
|
75
76
|
const ctx = mountedCtx
|
|
76
|
-
if (!ctx) return
|
|
77
|
+
if (!ctx || !enabled) return
|
|
77
78
|
try {
|
|
78
79
|
const status = await pi.exec('git', ['status', '--porcelain'], { cwd: ctx.cwd })
|
|
79
80
|
changedFiles = status.stdout.split('\n').filter((line) => line.trim().length > 0).length
|
|
@@ -87,7 +88,7 @@ export default function statuslineExtension(pi: ExtensionAPI): void {
|
|
|
87
88
|
|
|
88
89
|
async function refreshPr(): Promise<void> {
|
|
89
90
|
const ctx = mountedCtx
|
|
90
|
-
if (!ctx || !showPr) return
|
|
91
|
+
if (!ctx || !showPr || !enabled) return
|
|
91
92
|
try {
|
|
92
93
|
const pr = await pi.exec('gh', ['pr', 'view', '--json', 'number', '-q', '.number'], { cwd: ctx.cwd })
|
|
93
94
|
const parsed = Number.parseInt(pr.stdout.trim(), 10)
|
|
@@ -122,7 +123,7 @@ export default function statuslineExtension(pi: ExtensionAPI): void {
|
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
function mount(ctx: ExtensionContext): void {
|
|
125
|
-
if (!ctx.hasUI) return
|
|
126
|
+
if (!ctx.hasUI || !enabled) return
|
|
126
127
|
mountedCtx = ctx
|
|
127
128
|
|
|
128
129
|
void refreshGit()
|
package/src/timestamps/logic.ts
CHANGED
|
@@ -37,8 +37,6 @@ export function isToolCallMessage(message: { stopReason?: string; content?: { ty
|
|
|
37
37
|
)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const DUPLICATE_WINDOW_MS = 2_000
|
|
41
|
-
|
|
42
40
|
export function hasDuplicateTimestampEntry(
|
|
43
41
|
entries: readonly unknown[],
|
|
44
42
|
role: 'user' | 'assistant',
|
|
@@ -49,7 +47,7 @@ export function hasDuplicateTimestampEntry(
|
|
|
49
47
|
const entry = entries[i] as { type?: string; customType?: string; data?: TimestampEntryData } | undefined
|
|
50
48
|
if (!entry || entry.type !== 'custom' || entry.customType !== ENTRY_TYPE) continue
|
|
51
49
|
if (!entry.data || entry.data.role !== role) continue
|
|
52
|
-
if (
|
|
50
|
+
if (entry.data.timestamp === timestamp) return true
|
|
53
51
|
}
|
|
54
52
|
return false
|
|
55
53
|
}
|