opencode-dashboard-client 0.2.0 → 0.3.0

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "opencode-dashboard-client",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
- "description": "Front-end for the opencode token-usage dashboard. Ships the built SPA plus the front-end server (bun server.ts) that proxies to dashboard backends.",
5
+ "description": "Front-end for the opencode token-usage dashboard. Ships the built SPA plus a node CLI (opencode-dashboard serve/configure) that serves the SPA and proxies to dashboard backends.",
6
6
  "homepage": "https://github.com/GCS-ZHN/opencode-dashboard#readme",
7
7
  "repository": {
8
8
  "type": "git",
@@ -11,20 +11,22 @@
11
11
  "bugs": {
12
12
  "url": "https://github.com/GCS-ZHN/opencode-dashboard/issues"
13
13
  },
14
+ "bin": {
15
+ "opencode-dashboard": "dist-cli/cli.mjs"
16
+ },
14
17
  "files": [
15
18
  "dist",
16
- "server.ts",
17
- "config.ts",
18
- "dashboard.yaml"
19
+ "dist-cli"
19
20
  ],
20
21
  "publishConfig": {
21
22
  "access": "public"
22
23
  },
23
24
  "scripts": {
24
25
  "dev": "vite",
25
- "build": "tsc && tsc -p tsconfig.node.json && vite build",
26
+ "build": "tsc && tsc -p tsconfig.node.json && vite build && bun build cli.ts --target=node --outfile=dist-cli/cli.mjs",
26
27
  "preview": "vite preview",
27
28
  "mock": "bun mock-server.ts",
29
+ "serve": "node dist-cli/cli.mjs serve",
28
30
  "prepublishOnly": "npm run build"
29
31
  },
30
32
  "dependencies": {
package/config.ts DELETED
@@ -1,45 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { dirname, join } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import YAML from "yaml";
5
-
6
- export interface ServerConfig {
7
- name: string;
8
- url: string;
9
- }
10
-
11
- export interface DashboardConfig {
12
- host?: string;
13
- port?: number;
14
- servers: ServerConfig[];
15
- ui?: { sessionPage?: number };
16
- }
17
-
18
- // Works under bun (import.meta.dir) and vite's config loader (import.meta.url).
19
- function baseDir(): string {
20
- try {
21
- return import.meta.dir ?? dirname(fileURLToPath(import.meta.url));
22
- } catch {
23
- return process.cwd();
24
- }
25
- }
26
-
27
- // Node-only module (front-end server / vite config / mock server). The browser
28
- // never imports this — the SPA fetches the resolved values from /api/config.
29
- export function loadDashboardConfig(
30
- path: string = process.env.DASHBOARD_CONFIG ?? join(baseDir(), "dashboard.yaml"),
31
- ): DashboardConfig {
32
- const doc = (YAML.parse(readFileSync(path, "utf8")) ?? {}) as DashboardConfig;
33
- const port = Number(process.env.PORT ?? doc.port ?? 5173);
34
- if (!Number.isInteger(port) || port < 1 || port > 65535) {
35
- throw new Error(`invalid port in ${path}: ${String(doc.port)}`);
36
- }
37
- const sessionPage = Number(doc.ui?.sessionPage ?? 30);
38
- return {
39
- host: process.env.HOST ?? doc.host ?? "0.0.0.0",
40
- port,
41
- servers: doc.servers ?? [],
42
- ui: { sessionPage: Number.isInteger(sessionPage) && sessionPage > 0 ? sessionPage : 30 },
43
- };
44
- }
45
-
package/dashboard.yaml DELETED
@@ -1,25 +0,0 @@
1
- # opencode-dashboard front-end server configuration.
2
- # The front-end server (bun server.ts) and the vite dev server read this file.
3
- # Everything a user might want to change lives here; env vars (PORT, HOST,
4
- # DASHBOARD_CONFIG) override the matching fields. The SPA receives the resolved
5
- # servers + ui options at runtime via GET /api/config (it never bundles them).
6
-
7
- # Front-end server bind address (env HOST wins; default 0.0.0.0)
8
- host: 0.0.0.0
9
- # Front-end server port (env PORT wins; default 5173)
10
- port: 5173
11
-
12
- # Aggregation backends to proxy as /api/s/{index}/*
13
- servers:
14
- - name: main
15
- url: http://127.0.0.1:8791
16
- - name: backup
17
- url: http://127.0.0.1:8792
18
- - name: dev
19
- url: http://127.0.0.1:8793
20
- - name: staging
21
- url: http://127.0.0.1:8794
22
-
23
- # Browser-side UI options, delivered to the SPA via /api/config
24
- ui:
25
- sessionPage: 30 # sessions loaded per page when a project is expanded
package/server.ts DELETED
@@ -1,57 +0,0 @@
1
- // Front-end server: single entry point for clients. Serves dist/ and proxies
2
- // /api/s/{i}/* to the configured backends — same route scheme as vite.config.ts,
3
- // so clients never reach the real backends (they may be unreachable from the
4
- // client's network). Configuration comes from dashboard.yaml (env overrides:
5
- // DASHBOARD_CONFIG, PORT, HOST). Run: bun server.ts
6
- import { serve } from "bun";
7
- import { join } from "node:path";
8
- import { loadDashboardConfig } from "./config";
9
-
10
- const cfg = loadDashboardConfig();
11
- const DIST = join(import.meta.dir, "dist");
12
-
13
- const MIME: Record<string, string> = {
14
- ".html": "text/html; charset=utf-8",
15
- ".js": "text/javascript",
16
- ".css": "text/css",
17
- ".svg": "image/svg+xml",
18
- ".json": "application/json",
19
- ".png": "image/png",
20
- };
21
-
22
- const file = async (path: string) => {
23
- const f = Bun.file(join(DIST, path));
24
- return (await f.exists()) ? f : null;
25
- };
26
-
27
- serve({
28
- hostname: cfg.host,
29
- port: cfg.port,
30
- async fetch(req) {
31
- const url = new URL(req.url);
32
-
33
- if (url.pathname === "/api/config") {
34
- return Response.json({ servers: cfg.servers, ui: cfg.ui });
35
- }
36
-
37
- const m = url.pathname.match(/^\/api\/s\/(\d+)(\/.*)?$/);
38
- if (m) {
39
- const target = cfg.servers[Number(m[1])];
40
- if (!target) return new Response("unknown server", { status: 404 });
41
- const res = await fetch(target.url + (m[2] ?? "") + url.search, req);
42
- return new Response(res.body, { status: res.status, headers: res.headers });
43
- }
44
-
45
- const path = url.pathname === "/" ? "/index.html" : url.pathname;
46
- const f = await file(path);
47
- if (f) {
48
- const ext = path.slice(path.lastIndexOf("."));
49
- return new Response(f, { headers: { "content-type": MIME[ext] ?? "application/octet-stream" } });
50
- }
51
- const idx = await file("index.html"); // SPA fallback
52
- if (idx) return new Response(idx, { headers: { "content-type": MIME[".html"] } });
53
- return new Response("not found", { status: 404 });
54
- },
55
- });
56
-
57
- console.log(`front-end server http://${cfg.host}:${cfg.port} (${cfg.servers.length} backends proxied)`);