opencode-brl-cost 0.1.0 → 0.1.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 +22 -9
- package/dist/index.js +39 -0
- package/dist/index.js.map +7 -0
- package/package.json +12 -10
- package/index.tsx +0 -77
package/README.md
CHANGED
|
@@ -4,14 +4,14 @@ OpenCode **TUI plugin** that pins the current session's cost to the bottom bar,
|
|
|
4
4
|
|
|
5
5
|
## How it works
|
|
6
6
|
|
|
7
|
-
- Renders a fixed status bar via the `app_bottom` TUI slot.
|
|
7
|
+
- Renders a fixed status bar via the `app_bottom` TUI slot (bottom-right of the screen).
|
|
8
8
|
- Shows **the active session's total cost** (`session.cost`, USD) converted with a **hardcoded rate of R$ 5,00 / US$ 1,00**.
|
|
9
|
-
-
|
|
9
|
+
- Falls back to `R$ 0,00` when there's no session or no cost.
|
|
10
10
|
- Currency formatting uses `Intl.NumberFormat` with `pt-BR` (e.g. `R$ 1,23`).
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Add the plugin to `tui.json`:
|
|
15
15
|
|
|
16
16
|
```json
|
|
17
17
|
{
|
|
@@ -19,26 +19,39 @@ TUI plugins are loaded from a file plugin or an npm package. Add the plugin to `
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Then restart OpenCode
|
|
22
|
+
Then **restart OpenCode**. The bottom bar appears at the bottom-right.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
> Pin a version for stability: `"opencode-brl-cost@0.1.2"`.
|
|
25
|
+
|
|
26
|
+
## Why this shape
|
|
27
|
+
|
|
28
|
+
Per the `qwert:opencode-plugin` skill, OpenCode plugins must ship a **built `.js`** via npm with an
|
|
29
|
+
`exports["./tui"]` entrypoint. The runtime does **not** transpile `.tsx`/`.ts` from `node_modules` — it
|
|
30
|
+
fails silently. So this package publishes `dist/index.js` (esbuild output), never raw `.tsx`.
|
|
26
31
|
|
|
27
32
|
## Local development
|
|
28
33
|
|
|
29
34
|
```bash
|
|
30
35
|
npm install
|
|
36
|
+
npm run build # → dist/index.js
|
|
31
37
|
npm run typecheck
|
|
32
38
|
```
|
|
33
39
|
|
|
34
|
-
To test
|
|
40
|
+
To test a local checkout without publishing, point `tui.json` at the built file:
|
|
35
41
|
|
|
36
42
|
```json
|
|
37
43
|
{
|
|
38
|
-
"plugin": ["file:///absolute/path/to/opencode-brl-cost/index.
|
|
44
|
+
"plugin": ["file:///absolute/path/to/opencode-brl-cost/dist/index.js"]
|
|
39
45
|
}
|
|
40
46
|
```
|
|
41
47
|
|
|
48
|
+
## Publish
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npm version patch
|
|
52
|
+
npm publish
|
|
53
|
+
```
|
|
54
|
+
|
|
42
55
|
## Roadmap
|
|
43
56
|
|
|
44
57
|
- Configurable USD→BRL rate (instead of the hardcoded 5)
|
|
@@ -47,4 +60,4 @@ To test locally, point `tui.json` at the source file:
|
|
|
47
60
|
|
|
48
61
|
## License
|
|
49
62
|
|
|
50
|
-
|
|
63
|
+
AGPL-3.0 · Copyright (C) 2025 oporpino <dev@porpi.no>
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/index.tsx
|
|
2
|
+
import { jsx } from "@opentui/solid/jsx-runtime";
|
|
3
|
+
var BRL_PER_USD = 5;
|
|
4
|
+
var formatBRL = (usd) => {
|
|
5
|
+
const rounded = Math.round(usd * BRL_PER_USD * 100) / 100;
|
|
6
|
+
return new Intl.NumberFormat("pt-BR", {
|
|
7
|
+
style: "currency",
|
|
8
|
+
currency: "BRL"
|
|
9
|
+
}).format(rounded);
|
|
10
|
+
};
|
|
11
|
+
var tui = async (api) => {
|
|
12
|
+
const currentSessionID = () => {
|
|
13
|
+
const route = api.route.current;
|
|
14
|
+
return route.name === "session" && typeof route.params?.sessionID === "string" ? route.params.sessionID : void 0;
|
|
15
|
+
};
|
|
16
|
+
const currentCostBRL = () => {
|
|
17
|
+
const sessionID = currentSessionID();
|
|
18
|
+
if (!sessionID) return formatBRL(0);
|
|
19
|
+
const session = api.state.session.get(sessionID);
|
|
20
|
+
return formatBRL(typeof session?.cost === "number" ? session.cost : 0);
|
|
21
|
+
};
|
|
22
|
+
api.slots.register({
|
|
23
|
+
slots: {
|
|
24
|
+
app_bottom(ctx) {
|
|
25
|
+
const theme = ctx.theme.current;
|
|
26
|
+
return /* @__PURE__ */ jsx("box", { flexDirection: "row", justifyContent: "flex-end", paddingRight: 2, children: /* @__PURE__ */ jsx("text", { fg: theme.textMuted, children: currentCostBRL() }) });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
var plugin = {
|
|
32
|
+
id: "opencode-brl-cost",
|
|
33
|
+
tui
|
|
34
|
+
};
|
|
35
|
+
var index_default = plugin;
|
|
36
|
+
export {
|
|
37
|
+
index_default as default
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { TuiPlugin, TuiPluginModule } from \"@opencode-ai/plugin/tui\"\nimport type { Session } from \"@opencode-ai/sdk/v2\"\n\nconst BRL_PER_USD = 5\n\nconst formatBRL = (usd: number) => {\n const rounded = Math.round(usd * BRL_PER_USD * 100) / 100\n return new Intl.NumberFormat(\"pt-BR\", {\n style: \"currency\",\n currency: \"BRL\",\n }).format(rounded)\n}\n\nconst tui: TuiPlugin = async (api) => {\n const currentSessionID = (): string | undefined => {\n const route = api.route.current\n return route.name === \"session\" && typeof route.params?.sessionID === \"string\"\n ? route.params.sessionID\n : undefined\n }\n\n const currentCostBRL = (): string => {\n const sessionID = currentSessionID()\n if (!sessionID) return formatBRL(0)\n const session = api.state.session.get(sessionID) as Session | undefined\n return formatBRL(typeof session?.cost === \"number\" ? session.cost : 0)\n }\n\n api.slots.register({\n slots: {\n app_bottom(ctx) {\n const theme = ctx.theme.current\n return (\n <box flexDirection=\"row\" justifyContent=\"flex-end\" paddingRight={2}>\n <text fg={theme.textMuted}>{currentCostBRL()}</text>\n </box>\n )\n },\n },\n })\n}\n\nconst plugin: TuiPluginModule = {\n id: \"opencode-brl-cost\",\n tui,\n}\n\nexport default plugin"],
|
|
5
|
+
"mappings": ";AAkCY;AA/BZ,IAAM,cAAc;AAEpB,IAAM,YAAY,CAAC,QAAgB;AACjC,QAAM,UAAU,KAAK,MAAM,MAAM,cAAc,GAAG,IAAI;AACtD,SAAO,IAAI,KAAK,aAAa,SAAS;AAAA,IACpC,OAAO;AAAA,IACP,UAAU;AAAA,EACZ,CAAC,EAAE,OAAO,OAAO;AACnB;AAEA,IAAM,MAAiB,OAAO,QAAQ;AACpC,QAAM,mBAAmB,MAA0B;AACjD,UAAM,QAAQ,IAAI,MAAM;AACxB,WAAO,MAAM,SAAS,aAAa,OAAO,MAAM,QAAQ,cAAc,WAClE,MAAM,OAAO,YACb;AAAA,EACN;AAEA,QAAM,iBAAiB,MAAc;AACnC,UAAM,YAAY,iBAAiB;AACnC,QAAI,CAAC,UAAW,QAAO,UAAU,CAAC;AAClC,UAAM,UAAU,IAAI,MAAM,QAAQ,IAAI,SAAS;AAC/C,WAAO,UAAU,OAAO,SAAS,SAAS,WAAW,QAAQ,OAAO,CAAC;AAAA,EACvE;AAEA,MAAI,MAAM,SAAS;AAAA,IACjB,OAAO;AAAA,MACL,WAAW,KAAK;AACd,cAAM,QAAQ,IAAI,MAAM;AACxB,eACE,oBAAC,SAAI,eAAc,OAAM,gBAAe,YAAW,cAAc,GAC/D,8BAAC,UAAK,IAAI,MAAM,WAAY,yBAAe,GAAE,GAC/C;AAAA,MAEJ;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,IAAM,SAA0B;AAAA,EAC9B,IAAI;AAAA,EACJ;AACF;AAEA,IAAO,gBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-brl-cost",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "OpenCode TUI plugin that shows the session cost in Brazilian Reais (R$)",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./index.
|
|
7
|
-
"files": [
|
|
8
|
-
"index.tsx"
|
|
9
|
-
],
|
|
6
|
+
"main": "./dist/index.js",
|
|
10
7
|
"exports": {
|
|
11
|
-
".": "./index.
|
|
12
|
-
"./tui": "./index.
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./tui": "./dist/index.js"
|
|
13
10
|
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
14
|
"scripts": {
|
|
15
|
+
"build": "node scripts/build.mjs",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
15
17
|
"typecheck": "tsc --noEmit"
|
|
16
18
|
},
|
|
17
19
|
"keywords": [
|
|
@@ -22,12 +24,12 @@
|
|
|
22
24
|
"brl",
|
|
23
25
|
"currency"
|
|
24
26
|
],
|
|
25
|
-
"license": "
|
|
27
|
+
"license": "AGPL-3.0",
|
|
28
|
+
"author": "oporpino <dev@porpi.no>",
|
|
26
29
|
"devDependencies": {
|
|
27
30
|
"@opencode-ai/plugin": "1.18.29",
|
|
28
31
|
"@opentui/solid": "0.4.5",
|
|
29
|
-
"
|
|
30
|
-
"solid-js": "^1.8.0",
|
|
32
|
+
"esbuild": "^0.28.0",
|
|
31
33
|
"typescript": "5.8.2"
|
|
32
34
|
}
|
|
33
35
|
}
|
package/index.tsx
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
import { createSignal } from "solid-js"
|
|
3
|
-
import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui"
|
|
4
|
-
|
|
5
|
-
const BRL_PER_USD = 5
|
|
6
|
-
|
|
7
|
-
const formatBRL = (usd: number) => {
|
|
8
|
-
const rounded = Math.round(usd * BRL_PER_USD * 100) / 100
|
|
9
|
-
return new Intl.NumberFormat("pt-BR", {
|
|
10
|
-
style: "currency",
|
|
11
|
-
currency: "BRL",
|
|
12
|
-
}).format(rounded)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const tui: TuiPlugin = async (api) => {
|
|
16
|
-
const [cost, setCost] = createSignal<string | undefined>(undefined)
|
|
17
|
-
const [busy, setBusy] = createSignal(false)
|
|
18
|
-
|
|
19
|
-
const currentSessionID = (): string | undefined => {
|
|
20
|
-
const route = api.route.current
|
|
21
|
-
return route.name === "session" && typeof route.params?.sessionID === "string"
|
|
22
|
-
? route.params.sessionID
|
|
23
|
-
: undefined
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const refresh = async () => {
|
|
27
|
-
const sessionID = currentSessionID()
|
|
28
|
-
if (!sessionID) {
|
|
29
|
-
setCost(undefined)
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Prefer the live SDK session (server truth, has cost); fall back to the store.
|
|
34
|
-
let usd: number | undefined
|
|
35
|
-
try {
|
|
36
|
-
const res = await api.client.session.get({ sessionID }, { throwOnError: true })
|
|
37
|
-
usd = res.data?.cost
|
|
38
|
-
} catch {
|
|
39
|
-
const session = api.state.session.get(sessionID)
|
|
40
|
-
usd = session?.cost
|
|
41
|
-
}
|
|
42
|
-
setCost(typeof usd === "number" ? formatBRL(usd) : undefined)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
void refresh()
|
|
46
|
-
|
|
47
|
-
api.event.on("session.updated", (event) => {
|
|
48
|
-
if (event.properties.sessionID === currentSessionID()) void refresh()
|
|
49
|
-
})
|
|
50
|
-
api.event.on("session.idle", (event) => {
|
|
51
|
-
if (event.properties.sessionID === currentSessionID()) void refresh()
|
|
52
|
-
})
|
|
53
|
-
api.event.on("tui.session.select", (event) => {
|
|
54
|
-
if (event.properties.sessionID === currentSessionID()) void refresh()
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
api.slots.register({
|
|
58
|
-
slots: {
|
|
59
|
-
app_bottom(ctx) {
|
|
60
|
-
const theme = ctx.theme.current
|
|
61
|
-
const value = cost() ?? formatBRL(0)
|
|
62
|
-
return (
|
|
63
|
-
<box flexDirection="row" justifyContent="flex-end" paddingRight={2} gap={1}>
|
|
64
|
-
<text fg={theme.textMuted}>{value}</text>
|
|
65
|
-
</box>
|
|
66
|
-
)
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const plugin: TuiPluginModule = {
|
|
73
|
-
id: "opencode-brl-cost",
|
|
74
|
-
tui,
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export default plugin
|