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 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
- - On the home screen (no session selected) the bar stays empty. It never shows a stale value.
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
- TUI plugins are loaded from a file plugin or an npm package. Add the plugin to `tui.json`:
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. The bottom bar appears when a session is open.
22
+ Then **restart OpenCode**. The bottom bar appears at the bottom-right.
23
23
 
24
- This repo is published as a `.tsx` source plugin (the format OpenCode's TUI loader transpiles natively),
25
- mirroring `@br4zz4/opencode-skill-picker`.
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 locally, point `tui.json` at the source file:
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.tsx"]
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
- MIT
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.0",
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.tsx",
7
- "files": [
8
- "index.tsx"
9
- ],
6
+ "main": "./dist/index.js",
10
7
  "exports": {
11
- ".": "./index.tsx",
12
- "./tui": "./index.tsx"
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": "MIT",
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
- "@opencode-ai/sdk": "1.18.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