new: [opencode-greenpt-usage] add GreenPT credit balance plugin
Show the remaining GreenPT API credit balance in the OpenCode TUI sidebar. The key is resolved from the ``apiKey`` option, the ``GREENPT_API_KEY`` environment variable, or the active OpenCode ``auth.json`` (provider ``greenpt``); the section is hidden when no key is present, so the plugin stays portable across machines and profiles. The balance is read from the ``x-credits-remaining`` header returned by a free ``HEAD /v1/chat/completions`` probe (HTTP 400 before any credit is consumed), which avoids a billable call per refresh. Tests cover the credential resolution, the header parsing and the live API. Distribute via git clone; there is no npm publish.
This commit is contained in:
49
test/format.test.ts
Normal file
49
test/format.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { formatBalanceError, formatCredits, readFoldOpen } from "../src/format"
|
||||
|
||||
describe("formatCredits", () => {
|
||||
it("formats with two decimals", () => {
|
||||
expect(formatCredits(5.309411174)).toBe("5.31")
|
||||
})
|
||||
|
||||
it("formats zero", () => {
|
||||
expect(formatCredits(0)).toBe("0.00")
|
||||
})
|
||||
|
||||
it("returns a dash for non-finite input", () => {
|
||||
expect(formatCredits(Number.NaN)).toBe("-")
|
||||
})
|
||||
})
|
||||
|
||||
describe("formatBalanceError", () => {
|
||||
it("renders each error kind", () => {
|
||||
expect(formatBalanceError({ kind: "unauthorized" })).toContain("invalid")
|
||||
expect(formatBalanceError({ kind: "network" })).toContain("network")
|
||||
expect(formatBalanceError({ kind: "missing-header", status: 400 })).toContain("400")
|
||||
expect(formatBalanceError({ kind: "no-credential", reason: "missing" })).toContain("GreenPT")
|
||||
})
|
||||
})
|
||||
|
||||
describe("readFoldOpen", () => {
|
||||
it("defaults to open when the KV store is absent", () => {
|
||||
expect(readFoldOpen(undefined)).toBe(true)
|
||||
})
|
||||
|
||||
it("reads a persisted boolean", () => {
|
||||
expect(readFoldOpen({ get: () => false })).toBe(false)
|
||||
})
|
||||
|
||||
it("falls back when the stored value is not a boolean", () => {
|
||||
expect(readFoldOpen({ get: () => "nope" })).toBe(true)
|
||||
})
|
||||
|
||||
it("survives a throwing KV store", () => {
|
||||
expect(
|
||||
readFoldOpen({
|
||||
get: () => {
|
||||
throw new Error("kv down")
|
||||
},
|
||||
}),
|
||||
).toBe(true)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user