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/README.md +30 -28
- package/dist-cli/cli.mjs +7305 -0
- package/package.json +8 -6
- package/config.ts +0 -45
- package/dashboard.yaml +0 -25
- package/server.ts +0 -57
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-dashboard-client",
|
|
3
|
-
"version": "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
|
|
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
|
-
"
|
|
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)`);
|