opencode-ufr 0.2.10 → 0.2.11

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.
Files changed (2) hide show
  1. package/dist/tui.js +155 -0
  2. package/package.json +8 -3
package/dist/tui.js ADDED
@@ -0,0 +1,155 @@
1
+ // @bun
2
+ // src/plugin/tui.staged.build.js
3
+ import { createComponent as _$createComponent } from "@opentui/solid";
4
+ import { insert as _$insert } from "@opentui/solid";
5
+ import { createElement as _$createElement } from "@opentui/solid";
6
+ import { Plugin } from "@opencode/plugin/tui";
7
+ import { createSignal, onCleanup, onMount, Show } from "solid-js";
8
+
9
+ // src/shared/fs.ts
10
+ import { mkdir, readFile, rename, rm, writeFile } from "fs/promises";
11
+ async function readText(file) {
12
+ try {
13
+ return await readFile(file, "utf8");
14
+ } catch (e) {
15
+ if (e.code === "ENOENT")
16
+ return null;
17
+ throw e;
18
+ }
19
+ }
20
+ async function readJson(file) {
21
+ const text = await readText(file);
22
+ if (text === null)
23
+ return null;
24
+ try {
25
+ return JSON.parse(text);
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ // src/shared/daemon-client.ts
32
+ async function readDaemonInfo(paths) {
33
+ const j = await readJson(paths.daemonFile);
34
+ if (j === null || typeof j.port !== "number" || typeof j.version !== "string")
35
+ return null;
36
+ return { port: j.port, pid: typeof j.pid === "number" ? j.pid : 0, version: j.version };
37
+ }
38
+ async function daemonRequest(paths, f, path, method = "GET") {
39
+ const info = await readDaemonInfo(paths);
40
+ const token = (await readText(paths.tokenFile))?.trim();
41
+ if (!info?.port || !token)
42
+ return null;
43
+ try {
44
+ return await f(`http://127.0.0.1:${info.port}${path}`, {
45
+ method,
46
+ headers: { Authorization: `Bearer ${token}` },
47
+ signal: AbortSignal.timeout(5000)
48
+ });
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ // src/shared/paths.ts
55
+ import { homedir } from "os";
56
+ import { join } from "path";
57
+ var APP = "opencode-ufr";
58
+ function resolvePaths(env = process.env, platform = process.platform, home = homedir()) {
59
+ let configDir, stateDir, cacheDir, dataDir;
60
+ if (env.OPENCODE_UFR_HOME) {
61
+ const base = env.OPENCODE_UFR_HOME;
62
+ configDir = join(base, "config");
63
+ stateDir = join(base, "state");
64
+ cacheDir = join(base, "cache");
65
+ dataDir = join(base, "data");
66
+ } else if (platform === "win32") {
67
+ const roaming = env.APPDATA ?? join(home, "AppData", "Roaming");
68
+ const local = env.LOCALAPPDATA ?? join(home, "AppData", "Local");
69
+ configDir = join(roaming, APP);
70
+ stateDir = join(local, APP, "state");
71
+ cacheDir = join(local, APP, "cache");
72
+ dataDir = join(local, APP, "data");
73
+ } else {
74
+ configDir = join(env.XDG_CONFIG_HOME ?? join(home, ".config"), APP);
75
+ stateDir = join(env.XDG_STATE_HOME ?? join(home, ".local", "state"), APP);
76
+ cacheDir = join(env.XDG_CACHE_HOME ?? join(home, ".cache"), APP);
77
+ dataDir = join(env.XDG_DATA_HOME ?? join(home, ".local", "share"), APP);
78
+ }
79
+ return {
80
+ configDir,
81
+ stateDir,
82
+ cacheDir,
83
+ dataDir,
84
+ configFile: join(configDir, "config.json"),
85
+ daemonFile: join(stateDir, "daemon.json"),
86
+ tokenFile: join(stateDir, "token"),
87
+ lockFile: join(stateDir, "daemon.lock"),
88
+ logFile: join(stateDir, "daemon.log"),
89
+ statsDb: join(dataDir, "stats.db"),
90
+ ufrModelsCache: join(cacheDir, "ufr-models.json")
91
+ };
92
+ }
93
+
94
+ // src/plugin/tui.staged.build.js
95
+ var POLL_MS = 1000;
96
+ function fmtTokens(v) {
97
+ return v >= 1000 ? `${(v / 1000).toFixed(1)}k` : String(v);
98
+ }
99
+ function RatesLine(props) {
100
+ const [rates, setRates] = createSignal(null);
101
+ let busy = false;
102
+ const poll = async () => {
103
+ if (busy)
104
+ return;
105
+ busy = true;
106
+ try {
107
+ const res = await daemonRequest(props.paths, (u, i) => fetch(u, i), "/v1/_status");
108
+ if (!res?.ok) {
109
+ setRates(null);
110
+ return;
111
+ }
112
+ const j = await res.json();
113
+ setRates(j.rates ?? null);
114
+ } finally {
115
+ busy = false;
116
+ }
117
+ };
118
+ let timer;
119
+ onMount(() => {
120
+ poll();
121
+ timer = setInterval(() => void poll(), POLL_MS);
122
+ });
123
+ onCleanup(() => clearInterval(timer));
124
+ const label = () => {
125
+ const r = rates();
126
+ if (!r)
127
+ return null;
128
+ return `UFR ${r.reqPerSec} req/s \xB7 ${fmtTokens(r.tokensOutPerSec)} tok/s out \xB7 ${fmtTokens(r.tokensInPerSec)} tok/s in`;
129
+ };
130
+ return _$createComponent(Show, {
131
+ get when() {
132
+ return label();
133
+ },
134
+ get children() {
135
+ var _el$ = _$createElement("text");
136
+ _$insert(_el$, label);
137
+ return _el$;
138
+ }
139
+ });
140
+ }
141
+ var tui_staged_build_default = Plugin.define({
142
+ id: "opencode-ufr.stats",
143
+ setup(context) {
144
+ const paths = resolvePaths();
145
+ return context.ui.slot({
146
+ append: "sidebar.content",
147
+ render: () => _$createComponent(RatesLine, {
148
+ paths
149
+ })
150
+ });
151
+ }
152
+ });
153
+ export {
154
+ tui_staged_build_default as default
155
+ };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "opencode-ufr",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "Uni Freiburg (UFR) Open WebUI models in opencode — local gateway with key pool, UFR-aware rate limiting, circuit breaker and fallbacks",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./src/plugin/index.ts",
8
8
  "exports": {
9
9
  ".": "./src/plugin/index.ts",
10
- "./tui": "./src/plugin/tui.tsx"
10
+ "./tui": "./dist/tui.js"
11
11
  },
12
12
  "dependencies": {
13
13
  "@opencode/plugin": "^2.0.23"
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "files": [
21
21
  "src",
22
+ "dist",
22
23
  "skills",
23
24
  "models.json",
24
25
  "README.md",
@@ -38,14 +39,18 @@
38
39
  "open-webui"
39
40
  ],
40
41
  "scripts": {
42
+ "build:tui": "bun scripts/build-tui.ts",
41
43
  "test": "bun test",
42
44
  "typecheck": "tsc --noEmit",
43
- "prepublishOnly": "bun run typecheck && bun test"
45
+ "prepublishOnly": "bun run build:tui && bun run typecheck && bun test"
44
46
  },
45
47
  "devDependencies": {
48
+ "@babel/core": "7.28.0",
49
+ "@babel/preset-typescript": "7.27.1",
46
50
  "@opentui/core": "^0.5.14",
47
51
  "@opentui/solid": "^0.5.14",
48
52
  "@types/bun": "^1.3.0",
53
+ "babel-preset-solid": "1.9.12",
49
54
  "solid-js": "^1.9.15",
50
55
  "typescript": "^5.9.0"
51
56
  }