bunqueue-dashboard 0.0.14

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 (71) hide show
  1. package/CHANGELOG.md +315 -0
  2. package/LICENSE +21 -0
  3. package/README.md +287 -0
  4. package/agent/db.ts +491 -0
  5. package/agent/dbQueryWorker.ts +21 -0
  6. package/agent/index.ts +47 -0
  7. package/agent/logger.ts +23 -0
  8. package/agent/manager.ts +262 -0
  9. package/agent/server.ts +213 -0
  10. package/dist/.vite/manifest.json +578 -0
  11. package/dist/assets/AddJob-DOtZrwc-.js +3 -0
  12. package/dist/assets/AreaChart-DzUXW8u3.js +1 -0
  13. package/dist/assets/Benchmark-BKVE509y.js +1 -0
  14. package/dist/assets/CopilotPanel-Bn8gPZx_.js +152 -0
  15. package/dist/assets/CopyButton-CSjndiY7.js +1 -0
  16. package/dist/assets/Cron-DzeoQTpD.js +1 -0
  17. package/dist/assets/CronManager-CI6wTFbe.js +1 -0
  18. package/dist/assets/Database-Bjkl_O9n.js +3 -0
  19. package/dist/assets/Diagnostics-BUeDbexv.js +1 -0
  20. package/dist/assets/Dlq-BzwhitJI.js +1 -0
  21. package/dist/assets/DlqControl-BUCut0SS.js +1 -0
  22. package/dist/assets/DlqPro-HoeGdD-B.js +1 -0
  23. package/dist/assets/Flows-D9sVq4vd.js +1 -0
  24. package/dist/assets/JobInspector-dB68adRz.js +2 -0
  25. package/dist/assets/Jobs-CAHFY5qa.js +1 -0
  26. package/dist/assets/JobsPro-BqIHonV6.js +1 -0
  27. package/dist/assets/Logs-DjNLXK_r.js +1 -0
  28. package/dist/assets/LogsPro-BPWf8AAv.js +1 -0
  29. package/dist/assets/McpServer-BVxK5LwU.js +22 -0
  30. package/dist/assets/Metrics-Bm_pwxzy.js +1 -0
  31. package/dist/assets/MetricsPro-DTsnXQVd.js +1 -0
  32. package/dist/assets/Overview-O67Y-R5x.js +1 -0
  33. package/dist/assets/Pagination-B-nfZjQZ.js +1 -0
  34. package/dist/assets/QueueControl-BrHXeXPc.js +1 -0
  35. package/dist/assets/QueueDetail-D-C5SXxB.js +1 -0
  36. package/dist/assets/Queues-5tq1o_1I.js +1 -0
  37. package/dist/assets/QueuesOverview-ClZM34rT.js +1 -0
  38. package/dist/assets/S3Backup-BZfckRKX.js +1 -0
  39. package/dist/assets/S3BackupPro-CteU25_J.js +1 -0
  40. package/dist/assets/ServerControl-BSLX2WeV.js +3 -0
  41. package/dist/assets/Settings-BAELEgud.js +1 -0
  42. package/dist/assets/Usage-B_8T34Sp.js +1 -0
  43. package/dist/assets/UsagePro-BhhM0H05.js +1 -0
  44. package/dist/assets/Webhooks-B_qJ0bx1.js +1 -0
  45. package/dist/assets/Workers-N6qIdxg1.js +1 -0
  46. package/dist/assets/WorkersPro-DzjKVhCO.js +1 -0
  47. package/dist/assets/cn-CMFYpumL.js +1 -0
  48. package/dist/assets/connectionStore-DI-LYcct.js +1 -0
  49. package/dist/assets/form-PtDLk33Z.js +1 -0
  50. package/dist/assets/index-ChJpSdwV.js +8 -0
  51. package/dist/assets/index-DNaP_bi8.css +1 -0
  52. package/dist/assets/install-BvB40Zk0.js +3 -0
  53. package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  54. package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  55. package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  56. package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  57. package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  58. package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  59. package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  60. package/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  61. package/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  62. package/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  63. package/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  64. package/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  65. package/dist/assets/jobActions-CuOdMglt.js +1 -0
  66. package/dist/assets/react-vendor-CHIHtdAC.js +11 -0
  67. package/dist/favicon.svg +27 -0
  68. package/dist/index.html +30 -0
  69. package/package.json +90 -0
  70. package/scripts/embedded.gen.ts +122 -0
  71. package/scripts/serve.ts +92 -0
@@ -0,0 +1,27 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" fill="none">
2
+ <title>bunqueue</title>
3
+ <defs>
4
+ <linearGradient id="box" x1="0" y1="0" x2="1" y2="1">
5
+ <stop offset="0%" stop-color="#f9a8d4"/>
6
+ <stop offset="100%" stop-color="#ec4899"/>
7
+ </linearGradient>
8
+ <linearGradient id="ear" x1="0" y1="0" x2="1" y2="1">
9
+ <stop offset="0%" stop-color="#fce7f3"/>
10
+ <stop offset="100%" stop-color="#fbcfe8"/>
11
+ </linearGradient>
12
+ </defs>
13
+ <rect x="2" y="2" width="96" height="96" rx="24" fill="url(#box)"/>
14
+ <g transform="translate(50,52) scale(0.86)">
15
+ <ellipse cx="-16" cy="-32" rx="8" ry="20" fill="#ffffff" opacity="0.92"/>
16
+ <ellipse cx="-16" cy="-32" rx="4" ry="14" fill="url(#ear)"/>
17
+ <ellipse cx="16" cy="-32" rx="8" ry="20" fill="#ffffff" opacity="0.92"/>
18
+ <ellipse cx="16" cy="-32" rx="4" ry="14" fill="url(#ear)"/>
19
+ <circle cx="0" cy="0" r="28" fill="#ffffff" opacity="0.97"/>
20
+ <ellipse cx="-10" cy="-4" rx="5" ry="6" fill="#18181b"/>
21
+ <ellipse cx="10" cy="-4" rx="5" ry="6" fill="#18181b"/>
22
+ <circle cx="-8" cy="-6" r="2" fill="#ffffff"/>
23
+ <circle cx="12" cy="-6" r="2" fill="#ffffff"/>
24
+ <ellipse cx="0" cy="8" rx="4" ry="3" fill="#be185d"/>
25
+ <path d="M -6 14 Q 0 20 6 14" stroke="#be185d" stroke-width="2.5" fill="none" stroke-linecap="round"/>
26
+ </g>
27
+ </svg>
@@ -0,0 +1,30 @@
1
+ <!doctype html>
2
+ <html lang="en" data-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="color-scheme" content="dark light" />
7
+ <script>
8
+ // Pre-paint theme: apply the persisted choice before the bundle loads so
9
+ // light-theme users don't get a dark first-paint flash on every visit.
10
+ // Key + shape must match themeStore (zustand persist: {state:{theme}}).
11
+ try {
12
+ const t = JSON.parse(localStorage.getItem('bq-dash-theme')).state.theme;
13
+ if (t === 'light' || t === 'dark') {
14
+ document.documentElement.dataset.theme = t;
15
+ document.documentElement.style.colorScheme = t;
16
+ }
17
+ } catch {}
18
+ </script>
19
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
20
+ <title>bunqueue · dashboard (beta)</title>
21
+ <script type="module" crossorigin src="/assets/index-ChJpSdwV.js"></script>
22
+ <link rel="modulepreload" crossorigin href="/assets/cn-CMFYpumL.js">
23
+ <link rel="modulepreload" crossorigin href="/assets/react-vendor-CHIHtdAC.js">
24
+ <link rel="modulepreload" crossorigin href="/assets/connectionStore-DI-LYcct.js">
25
+ <link rel="stylesheet" crossorigin href="/assets/index-DNaP_bi8.css">
26
+ </head>
27
+ <body>
28
+ <div id="root"></div>
29
+ </body>
30
+ </html>
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "bunqueue-dashboard",
3
+ "version": "0.0.14",
4
+ "type": "module",
5
+ "description": "Web dashboard for a bunqueue server (queues, jobs, DLQ, cron, metrics, workers, live activity). Run with `bunx bunqueue-dashboard` (requires Bun).",
6
+ "bin": {
7
+ "bunqueue-dashboard": "scripts/serve.ts"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "scripts/serve.ts",
12
+ "scripts/embedded.gen.ts",
13
+ "agent",
14
+ "CHANGELOG.md"
15
+ ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "license": "MIT",
20
+ "author": "Egeo Minotti",
21
+ "homepage": "https://github.com/egeominotti/bunqueue-dashboard#readme",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/egeominotti/bunqueue-dashboard.git"
25
+ },
26
+ "bugs": {
27
+ "url": "https://github.com/egeominotti/bunqueue-dashboard/issues"
28
+ },
29
+ "keywords": [
30
+ "bunqueue",
31
+ "dashboard",
32
+ "queue",
33
+ "jobs",
34
+ "react",
35
+ "vite",
36
+ "bun"
37
+ ],
38
+ "engines": {
39
+ "bun": ">=1.3.0"
40
+ },
41
+ "scripts": {
42
+ "prepack": "bun run build && bun scripts/gen-embed.ts",
43
+ "start": "bun run scripts/dev.ts",
44
+ "dev": "vite",
45
+ "agent": "bun run agent/index.ts",
46
+ "build": "tsc --noEmit && vite build",
47
+ "size": "bun run scripts/check-bundle.ts",
48
+ "docs:dev": "vitepress dev docs",
49
+ "docs:build": "vitepress build docs",
50
+ "docs:preview": "vitepress preview docs",
51
+ "build:bin": "bun run build && bun run scripts/gen-embed.ts && bun build scripts/serve.ts --compile --minify --outfile bunqueue-dashboard",
52
+ "preview": "vite preview",
53
+ "test": "bun test",
54
+ "check": "biome check .",
55
+ "check:fix": "biome check --write .",
56
+ "format": "biome format --write ."
57
+ },
58
+ "dependencies": {
59
+ "pino": "^10.3.1",
60
+ "pino-pretty": "^13.1.3"
61
+ },
62
+ "devDependencies": {
63
+ "@ai-sdk/anthropic": "^4.0.7",
64
+ "@ai-sdk/google": "^4.0.8",
65
+ "@ai-sdk/openai": "^4.0.7",
66
+ "@ai-sdk/openai-compatible": "^3.0.5",
67
+ "@fontsource-variable/inter": "^5.2.8",
68
+ "@fontsource-variable/jetbrains-mono": "^5.2.8",
69
+ "ai": "^7.0.14",
70
+ "react": "^19.2.7",
71
+ "react-dom": "^19.2.7",
72
+ "react-router-dom": "^7.18.1",
73
+ "zod": "^4.4.3",
74
+ "zustand": "^5.0.14",
75
+ "@biomejs/biome": "^2.5.2",
76
+ "@shikijs/vitepress-twoslash": "^4.3.0",
77
+ "@tailwindcss/vite": "^4.3.2",
78
+ "@types/node": "^26.0.1",
79
+ "@types/react": "^19.2.17",
80
+ "@types/react-dom": "^19.2.3",
81
+ "@vitejs/plugin-react": "^6.0.3",
82
+ "mermaid": "^11.16.0",
83
+ "tailwindcss": "^4.3.2",
84
+ "typescript": "^6.0.3",
85
+ "vite": "^8.1.2",
86
+ "vitepress": "^1.6.4",
87
+ "vitepress-plugin-llms": "^1.13.2",
88
+ "vitepress-plugin-mermaid": "^2.0.17"
89
+ }
90
+ }
@@ -0,0 +1,122 @@
1
+ // AUTO-GENERATED by scripts/gen-embed.ts — do not edit, not committed.
2
+ import f0 from "../dist/.vite/manifest.json" with { type: 'file' };
3
+ import f1 from "../dist/favicon.svg" with { type: 'file' };
4
+ import f2 from "../dist/assets/QueueDetail-D-C5SXxB.js" with { type: 'file' };
5
+ import f3 from "../dist/assets/LogsPro-BPWf8AAv.js" with { type: 'file' };
6
+ import f4 from "../dist/assets/UsagePro-BhhM0H05.js" with { type: 'file' };
7
+ import f5 from "../dist/assets/Cron-DzeoQTpD.js" with { type: 'file' };
8
+ import f6 from "../dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2" with { type: 'file' };
9
+ import f7 from "../dist/assets/Dlq-BzwhitJI.js" with { type: 'file' };
10
+ import f8 from "../dist/assets/Logs-DjNLXK_r.js" with { type: 'file' };
11
+ import f9 from "../dist/assets/AreaChart-DzUXW8u3.js" with { type: 'file' };
12
+ import f10 from "../dist/assets/Diagnostics-BUeDbexv.js" with { type: 'file' };
13
+ import f11 from "../dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2" with { type: 'file' };
14
+ import f12 from "../dist/assets/Overview-O67Y-R5x.js" with { type: 'file' };
15
+ import f13 from "../dist/assets/ServerControl-BSLX2WeV.js" with { type: 'file' };
16
+ import f14 from "../dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2" with { type: 'file' };
17
+ import f15 from "../dist/assets/McpServer-BVxK5LwU.js" with { type: 'file' };
18
+ import f16 from "../dist/assets/Usage-B_8T34Sp.js" with { type: 'file' };
19
+ import f17 from "../dist/assets/form-PtDLk33Z.js" with { type: 'file' };
20
+ import f18 from "../dist/assets/Benchmark-BKVE509y.js" with { type: 'file' };
21
+ import f19 from "../dist/assets/QueuesOverview-ClZM34rT.js" with { type: 'file' };
22
+ import f20 from "../dist/assets/react-vendor-CHIHtdAC.js" with { type: 'file' };
23
+ import f21 from "../dist/assets/Workers-N6qIdxg1.js" with { type: 'file' };
24
+ import f22 from "../dist/assets/DlqControl-BUCut0SS.js" with { type: 'file' };
25
+ import f23 from "../dist/assets/JobInspector-dB68adRz.js" with { type: 'file' };
26
+ import f24 from "../dist/assets/jobActions-CuOdMglt.js" with { type: 'file' };
27
+ import f25 from "../dist/assets/WorkersPro-DzjKVhCO.js" with { type: 'file' };
28
+ import f26 from "../dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2" with { type: 'file' };
29
+ import f27 from "../dist/assets/Webhooks-B_qJ0bx1.js" with { type: 'file' };
30
+ import f28 from "../dist/assets/CopilotPanel-Bn8gPZx_.js" with { type: 'file' };
31
+ import f29 from "../dist/assets/connectionStore-DI-LYcct.js" with { type: 'file' };
32
+ import f30 from "../dist/assets/JobsPro-BqIHonV6.js" with { type: 'file' };
33
+ import f31 from "../dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2" with { type: 'file' };
34
+ import f32 from "../dist/assets/QueueControl-BrHXeXPc.js" with { type: 'file' };
35
+ import f33 from "../dist/assets/Pagination-B-nfZjQZ.js" with { type: 'file' };
36
+ import f34 from "../dist/assets/Queues-5tq1o_1I.js" with { type: 'file' };
37
+ import f35 from "../dist/assets/cn-CMFYpumL.js" with { type: 'file' };
38
+ import f36 from "../dist/assets/CronManager-CI6wTFbe.js" with { type: 'file' };
39
+ import f37 from "../dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2" with { type: 'file' };
40
+ import f38 from "../dist/assets/CopyButton-CSjndiY7.js" with { type: 'file' };
41
+ import f39 from "../dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2" with { type: 'file' };
42
+ import f40 from "../dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2" with { type: 'file' };
43
+ import f41 from "../dist/assets/Database-Bjkl_O9n.js" with { type: 'file' };
44
+ import f42 from "../dist/assets/AddJob-DOtZrwc-.js" with { type: 'file' };
45
+ import f43 from "../dist/assets/Settings-BAELEgud.js" with { type: 'file' };
46
+ import f44 from "../dist/assets/S3BackupPro-CteU25_J.js" with { type: 'file' };
47
+ import f45 from "../dist/assets/Metrics-Bm_pwxzy.js" with { type: 'file' };
48
+ import f46 from "../dist/assets/MetricsPro-DTsnXQVd.js" with { type: 'file' };
49
+ import f47 from "../dist/assets/DlqPro-HoeGdD-B.js" with { type: 'file' };
50
+ import f48 from "../dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2" with { type: 'file' };
51
+ import f49 from "../dist/assets/index-ChJpSdwV.js" with { type: 'file' };
52
+ import f50 from "../dist/assets/index-DNaP_bi8.css" with { type: 'file' };
53
+ import f51 from "../dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2" with { type: 'file' };
54
+ import f52 from "../dist/assets/install-BvB40Zk0.js" with { type: 'file' };
55
+ import f53 from "../dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2" with { type: 'file' };
56
+ import f54 from "../dist/assets/S3Backup-BZfckRKX.js" with { type: 'file' };
57
+ import f55 from "../dist/assets/Flows-D9sVq4vd.js" with { type: 'file' };
58
+ import f56 from "../dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2" with { type: 'file' };
59
+ import f57 from "../dist/assets/Jobs-CAHFY5qa.js" with { type: 'file' };
60
+ import f58 from "../dist/index.html" with { type: 'file' };
61
+
62
+ export const ASSETS: Record<string, string> = {
63
+ "/.vite/manifest.json": f0,
64
+ "/favicon.svg": f1,
65
+ "/assets/QueueDetail-D-C5SXxB.js": f2,
66
+ "/assets/LogsPro-BPWf8AAv.js": f3,
67
+ "/assets/UsagePro-BhhM0H05.js": f4,
68
+ "/assets/Cron-DzeoQTpD.js": f5,
69
+ "/assets/inter-greek-wght-normal-CkhJZR-_.woff2": f6,
70
+ "/assets/Dlq-BzwhitJI.js": f7,
71
+ "/assets/Logs-DjNLXK_r.js": f8,
72
+ "/assets/AreaChart-DzUXW8u3.js": f9,
73
+ "/assets/Diagnostics-BUeDbexv.js": f10,
74
+ "/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2": f11,
75
+ "/assets/Overview-O67Y-R5x.js": f12,
76
+ "/assets/ServerControl-BSLX2WeV.js": f13,
77
+ "/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2": f14,
78
+ "/assets/McpServer-BVxK5LwU.js": f15,
79
+ "/assets/Usage-B_8T34Sp.js": f16,
80
+ "/assets/form-PtDLk33Z.js": f17,
81
+ "/assets/Benchmark-BKVE509y.js": f18,
82
+ "/assets/QueuesOverview-ClZM34rT.js": f19,
83
+ "/assets/react-vendor-CHIHtdAC.js": f20,
84
+ "/assets/Workers-N6qIdxg1.js": f21,
85
+ "/assets/DlqControl-BUCut0SS.js": f22,
86
+ "/assets/JobInspector-dB68adRz.js": f23,
87
+ "/assets/jobActions-CuOdMglt.js": f24,
88
+ "/assets/WorkersPro-DzjKVhCO.js": f25,
89
+ "/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2": f26,
90
+ "/assets/Webhooks-B_qJ0bx1.js": f27,
91
+ "/assets/CopilotPanel-Bn8gPZx_.js": f28,
92
+ "/assets/connectionStore-DI-LYcct.js": f29,
93
+ "/assets/JobsPro-BqIHonV6.js": f30,
94
+ "/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2": f31,
95
+ "/assets/QueueControl-BrHXeXPc.js": f32,
96
+ "/assets/Pagination-B-nfZjQZ.js": f33,
97
+ "/assets/Queues-5tq1o_1I.js": f34,
98
+ "/assets/cn-CMFYpumL.js": f35,
99
+ "/assets/CronManager-CI6wTFbe.js": f36,
100
+ "/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2": f37,
101
+ "/assets/CopyButton-CSjndiY7.js": f38,
102
+ "/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2": f39,
103
+ "/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2": f40,
104
+ "/assets/Database-Bjkl_O9n.js": f41,
105
+ "/assets/AddJob-DOtZrwc-.js": f42,
106
+ "/assets/Settings-BAELEgud.js": f43,
107
+ "/assets/S3BackupPro-CteU25_J.js": f44,
108
+ "/assets/Metrics-Bm_pwxzy.js": f45,
109
+ "/assets/MetricsPro-DTsnXQVd.js": f46,
110
+ "/assets/DlqPro-HoeGdD-B.js": f47,
111
+ "/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2": f48,
112
+ "/assets/index-ChJpSdwV.js": f49,
113
+ "/assets/index-DNaP_bi8.css": f50,
114
+ "/assets/inter-latin-wght-normal-Dx4kXJAl.woff2": f51,
115
+ "/assets/install-BvB40Zk0.js": f52,
116
+ "/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2": f53,
117
+ "/assets/S3Backup-BZfckRKX.js": f54,
118
+ "/assets/Flows-D9sVq4vd.js": f55,
119
+ "/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2": f56,
120
+ "/assets/Jobs-CAHFY5qa.js": f57,
121
+ "/index.html": f58,
122
+ };
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Standalone bunqueue-dashboard executable (built with `bun build --compile`).
4
+ *
5
+ * One binary, three jobs:
6
+ * 1. Serves the dashboard SPA from assets embedded at compile time.
7
+ * 2. Proxies /api/* to the bunqueue server (BUNQUEUE_URL, default :6790) —
8
+ * same-origin like the dev proxy, so no CORS and SSE streams through.
9
+ * 3. Runs the control agent (start/stop/restart bunqueue) on 127.0.0.1:6800
10
+ * with the same Origin-allowlist security as agent/index.ts.
11
+ *
12
+ * Env: PORT (dashboard, default 8080) · BIND_ADDR (default 127.0.0.1) ·
13
+ * BUNQUEUE_URL · AGENT_PORT · AGENT_ALLOWED_ORIGINS · AGENT_TOKEN ·
14
+ * BUNQUEUE_START_CMD · HTTP_PORT · TCP_PORT · BUNQUEUE_DATA_PATH ·
15
+ * LOG_LEVEL (pino level, default info)
16
+ */
17
+ import { logger } from '../agent/logger';
18
+ import { ProcessManager } from '../agent/manager';
19
+ import { createFetchHandler, resolveAllowedOrigins } from '../agent/server';
20
+ import { ASSETS } from './embedded.gen';
21
+
22
+ const PORT = Number(process.env.PORT) || 8080;
23
+ // Bind loopback by default: the /api proxy forwards to bunqueue's admin API, so
24
+ // listening on all interfaces would expose it to the whole network. Set
25
+ // BIND_ADDR=0.0.0.0 for direct LAN access (e.g. no reverse proxy in front).
26
+ const HOST = process.env.BIND_ADDR || '127.0.0.1';
27
+ const API = (process.env.BUNQUEUE_URL || 'http://localhost:6790').replace(/\/$/, '');
28
+ const AGENT_PORT = Number(process.env.AGENT_PORT) || 6800;
29
+
30
+ // Control agent — loopback only, allowlisted CORS; the origins this binary
31
+ // serves the dashboard from are allowed automatically.
32
+ const allowedOrigins = Array.from(
33
+ new Set([
34
+ ...resolveAllowedOrigins(),
35
+ `http://localhost:${PORT}`,
36
+ `http://127.0.0.1:${PORT}`,
37
+ ])
38
+ );
39
+ const mgr = new ProcessManager();
40
+ const agentHandle = createFetchHandler(mgr, {
41
+ allowedOrigins,
42
+ token: process.env.AGENT_TOKEN || undefined,
43
+ });
44
+ Bun.serve({ port: AGENT_PORT, hostname: '127.0.0.1', fetch: agentHandle });
45
+
46
+ // Dashboard + /api proxy.
47
+ Bun.serve({
48
+ port: PORT,
49
+ hostname: HOST,
50
+ async fetch(req) {
51
+ const url = new URL(req.url);
52
+
53
+ // Same-origin proxy to the bunqueue server (mirrors the Vite dev proxy).
54
+ if (url.pathname === '/api' || url.pathname.startsWith('/api/')) {
55
+ const target = API + (url.pathname.slice(4) || '/') + url.search;
56
+ const res = await fetch(target, {
57
+ method: req.method,
58
+ headers: req.headers,
59
+ body: req.body,
60
+ redirect: 'manual',
61
+ });
62
+ // Bun's fetch advertises accept-encoding upstream and transparently
63
+ // DECOMPRESSES the body, but leaves the upstream headers intact. If the
64
+ // bunqueue server sits behind any gzip-compressing proxy, forwarding
65
+ // those headers labels plaintext as gzip and the browser fails to decode
66
+ // every /api response (ERR_CONTENT_DECODING_FAILED) — strip them.
67
+ const headers = new Headers(res.headers);
68
+ headers.delete('content-encoding');
69
+ headers.delete('content-length');
70
+ headers.delete('transfer-encoding');
71
+ return new Response(res.body, { status: res.status, statusText: res.statusText, headers });
72
+ }
73
+
74
+ // Embedded static assets with SPA history fallback. A missing fingerprinted
75
+ // /assets/* file must 404 (as docker/Caddyfile does) — falling back to
76
+ // index.html would feed HTML to a stale chunk import() and mask the miss.
77
+ const key = url.pathname === '/' ? '/index.html' : url.pathname;
78
+ const asset =
79
+ ASSETS[key] ?? (url.pathname.startsWith('/assets/') ? undefined : ASSETS['/index.html']);
80
+ if (!asset) return new Response('Not found', { status: 404 });
81
+ return new Response(Bun.file(asset));
82
+ },
83
+ });
84
+
85
+ logger.info(
86
+ {
87
+ dashboard: `http://${HOST}:${PORT}`,
88
+ apiProxy: API,
89
+ agent: `http://127.0.0.1:${AGENT_PORT}/control`,
90
+ },
91
+ 'bunqueue dashboard (standalone) ready'
92
+ );