openwolf-enhanced 1.9.3
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/CHANGELOG.md +386 -0
- package/LICENSE +663 -0
- package/NOTICE +48 -0
- package/README.md +283 -0
- package/dist/bin/openwolf.js +10 -0
- package/dist/bin/openwolf.js.map +1 -0
- package/dist/dashboard/assets/AISuggestions-DHXOgHMM.js +9 -0
- package/dist/dashboard/assets/ActivityTimeline-BXLwacZV.js +1 -0
- package/dist/dashboard/assets/AnatomyBrowser-CUyC8M2I.js +1 -0
- package/dist/dashboard/assets/BugLog-Bxvy1sPW.js +1 -0
- package/dist/dashboard/assets/CerebrumViewer-DCySVmzA.js +1 -0
- package/dist/dashboard/assets/CronStatus-BA4fmQX3.js +1 -0
- package/dist/dashboard/assets/DesignQC-BhNEKryS.js +1 -0
- package/dist/dashboard/assets/MemoryViewer-0y9lsDUO.js +1 -0
- package/dist/dashboard/assets/ProjectOverview-CC51rVRe.js +1 -0
- package/dist/dashboard/assets/StatusBadge-4NJI_F2G.js +1 -0
- package/dist/dashboard/assets/TokenUsage-7pCxlnCX.js +55 -0
- package/dist/dashboard/assets/index-4O5rk-qB.css +2 -0
- package/dist/dashboard/assets/index-jfgWxxsn.js +13 -0
- package/dist/dashboard/assets/utils-BqE_aDxV.js +1 -0
- package/dist/dashboard/index.html +16 -0
- package/dist/hooks/post-read.js +85 -0
- package/dist/hooks/post-write.js +520 -0
- package/dist/hooks/pre-read.js +107 -0
- package/dist/hooks/pre-write.js +120 -0
- package/dist/hooks/session-start.js +76 -0
- package/dist/hooks/shared.js +0 -0
- package/dist/hooks/stop.js +219 -0
- package/dist/src/buglog/bug-matcher.js +3 -0
- package/dist/src/buglog/bug-matcher.js.map +1 -0
- package/dist/src/buglog/bug-tracker.js +89 -0
- package/dist/src/buglog/bug-tracker.js.map +1 -0
- package/dist/src/cli/bug-cmd.js +31 -0
- package/dist/src/cli/bug-cmd.js.map +1 -0
- package/dist/src/cli/cron-cmd.js +107 -0
- package/dist/src/cli/cron-cmd.js.map +1 -0
- package/dist/src/cli/daemon-cmd.js +180 -0
- package/dist/src/cli/daemon-cmd.js.map +1 -0
- package/dist/src/cli/dashboard.js +88 -0
- package/dist/src/cli/dashboard.js.map +1 -0
- package/dist/src/cli/designqc-cmd.js +31 -0
- package/dist/src/cli/designqc-cmd.js.map +1 -0
- package/dist/src/cli/doctor-cmd.js +94 -0
- package/dist/src/cli/doctor-cmd.js.map +1 -0
- package/dist/src/cli/index.js +158 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/init.js +493 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/registry.js +93 -0
- package/dist/src/cli/registry.js.map +1 -0
- package/dist/src/cli/scan.js +39 -0
- package/dist/src/cli/scan.js.map +1 -0
- package/dist/src/cli/status.js +98 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/update.js +435 -0
- package/dist/src/cli/update.js.map +1 -0
- package/dist/src/daemon/cron-engine.js +321 -0
- package/dist/src/daemon/cron-engine.js.map +1 -0
- package/dist/src/daemon/file-watcher.js +64 -0
- package/dist/src/daemon/file-watcher.js.map +1 -0
- package/dist/src/daemon/health.js +23 -0
- package/dist/src/daemon/health.js.map +1 -0
- package/dist/src/daemon/wolf-daemon.js +440 -0
- package/dist/src/daemon/wolf-daemon.js.map +1 -0
- package/dist/src/designqc/designqc-capture.js +278 -0
- package/dist/src/designqc/designqc-capture.js.map +1 -0
- package/dist/src/designqc/designqc-engine.js +159 -0
- package/dist/src/designqc/designqc-engine.js.map +1 -0
- package/dist/src/designqc/designqc-types.js +5 -0
- package/dist/src/designqc/designqc-types.js.map +1 -0
- package/dist/src/hooks/post-read.js +86 -0
- package/dist/src/hooks/post-read.js.map +1 -0
- package/dist/src/hooks/post-write.js +521 -0
- package/dist/src/hooks/post-write.js.map +1 -0
- package/dist/src/hooks/pre-read.js +108 -0
- package/dist/src/hooks/pre-read.js.map +1 -0
- package/dist/src/hooks/pre-write.js +121 -0
- package/dist/src/hooks/pre-write.js.map +1 -0
- package/dist/src/hooks/session-start.js +77 -0
- package/dist/src/hooks/session-start.js.map +1 -0
- package/dist/src/hooks/shared.js +0 -0
- package/dist/src/hooks/shared.js.map +1 -0
- package/dist/src/hooks/stop.js +220 -0
- package/dist/src/hooks/stop.js.map +1 -0
- package/dist/src/scanner/anatomy-scanner.js +278 -0
- package/dist/src/scanner/anatomy-scanner.js.map +1 -0
- package/dist/src/scanner/description-extractor.js +1007 -0
- package/dist/src/scanner/description-extractor.js.map +1 -0
- package/dist/src/scanner/project-root.js +52 -0
- package/dist/src/scanner/project-root.js.map +1 -0
- package/dist/src/tracker/token-estimator.js +20 -0
- package/dist/src/tracker/token-estimator.js.map +1 -0
- package/dist/src/tracker/token-ledger.js +55 -0
- package/dist/src/tracker/token-ledger.js.map +1 -0
- package/dist/src/tracker/waste-detector.js +101 -0
- package/dist/src/tracker/waste-detector.js.map +1 -0
- package/dist/src/utils/dashboard-auth.js +50 -0
- package/dist/src/utils/dashboard-auth.js.map +1 -0
- package/dist/src/utils/fs-safe.js +135 -0
- package/dist/src/utils/fs-safe.js.map +1 -0
- package/dist/src/utils/hooks-deploy.js +70 -0
- package/dist/src/utils/hooks-deploy.js.map +1 -0
- package/dist/src/utils/logger.js +48 -0
- package/dist/src/utils/logger.js.map +1 -0
- package/dist/src/utils/maintenance.js +385 -0
- package/dist/src/utils/maintenance.js.map +1 -0
- package/dist/src/utils/paths.js +23 -0
- package/dist/src/utils/paths.js.map +1 -0
- package/dist/src/utils/platform.js +14 -0
- package/dist/src/utils/platform.js.map +1 -0
- package/dist/src/utils/seed.js +61 -0
- package/dist/src/utils/seed.js.map +1 -0
- package/package.json +82 -0
- package/src/templates/OPENWOLF.md +147 -0
- package/src/templates/STATUS.md +48 -0
- package/src/templates/anatomy.md +5 -0
- package/src/templates/buglog.json +4 -0
- package/src/templates/cerebrum.md +22 -0
- package/src/templates/claude-md-snippet.md +5 -0
- package/src/templates/claude-rules-openwolf.md +17 -0
- package/src/templates/config.json +83 -0
- package/src/templates/cron-manifest.json +97 -0
- package/src/templates/cron-state.json +7 -0
- package/src/templates/identity.md +9 -0
- package/src/templates/memory.md +4 -0
- package/src/templates/reframe-frameworks.md +597 -0
- package/src/templates/token-ledger.json +21 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import express from "express";
|
|
5
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
6
|
+
import { findProjectRoot } from "../scanner/project-root.js";
|
|
7
|
+
import { readJSON, writeJSON } from "../utils/fs-safe.js";
|
|
8
|
+
import { ensureDashboardToken, tokenMatches } from "../utils/dashboard-auth.js";
|
|
9
|
+
import { Logger } from "../utils/logger.js";
|
|
10
|
+
import { CronEngine } from "./cron-engine.js";
|
|
11
|
+
import { startFileWatcher } from "./file-watcher.js";
|
|
12
|
+
import { DesignQCEngine } from "../designqc/designqc-engine.js";
|
|
13
|
+
import { DEFAULT_VIEWPORTS } from "../designqc/designqc-types.js";
|
|
14
|
+
import { getRegisteredProjects } from "../cli/registry.js";
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
// Prefer explicit OPENWOLF_PROJECT_ROOT env (set by CLI commands) over cwd detection.
|
|
18
|
+
// projectRoot/wolfDir are mutable so /api/switch can hot-reload another project in place.
|
|
19
|
+
// The dashboard token, by contrast, is per-daemon (not re-read on switch) so an authenticated
|
|
20
|
+
// dashboard session survives a project switch.
|
|
21
|
+
let projectRoot = process.env.OPENWOLF_PROJECT_ROOT || findProjectRoot();
|
|
22
|
+
let wolfDir = path.join(projectRoot, ".wolf");
|
|
23
|
+
const config = readJSON(path.join(wolfDir, "config.json"), {
|
|
24
|
+
openwolf: {
|
|
25
|
+
daemon: { port: 18790, log_level: "info" },
|
|
26
|
+
dashboard: { enabled: true, port: 18791 },
|
|
27
|
+
cron: { enabled: true, heartbeat_interval_minutes: 30 },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
// Per-project dashboard token, required on every /api/* request and WS connection.
|
|
31
|
+
const dashboardToken = ensureDashboardToken(wolfDir);
|
|
32
|
+
const logger = new Logger(path.join(wolfDir, "daemon.log"), config.openwolf.daemon.log_level);
|
|
33
|
+
const startTime = Date.now();
|
|
34
|
+
const wsClients = new Set();
|
|
35
|
+
// Files served to the dashboard on demand (via /api/files and full_state broadcasts).
|
|
36
|
+
const WOLF_BROADCAST_FILES = [
|
|
37
|
+
"OPENWOLF.md", "identity.md", "cerebrum.md", "memory.md", "anatomy.md",
|
|
38
|
+
"config.json", "token-ledger.json", "buglog.json",
|
|
39
|
+
"cron-manifest.json", "cron-state.json",
|
|
40
|
+
"designqc-report.json",
|
|
41
|
+
];
|
|
42
|
+
// Read a .wolf file for delivery to the dashboard, trimming the largest ones so a
|
|
43
|
+
// multi-MB token-ledger.json isn't shipped in full over HTTP/WS on every connect.
|
|
44
|
+
// The dashboard only needs lifetime aggregates + recent sessions, so we keep the
|
|
45
|
+
// last 50 sessions when the file is large.
|
|
46
|
+
function readWolfFileForDashboard(file) {
|
|
47
|
+
let raw;
|
|
48
|
+
try {
|
|
49
|
+
raw = fs.readFileSync(path.join(wolfDir, file), "utf-8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
if (file === "token-ledger.json" && raw.length > 256 * 1024) {
|
|
55
|
+
try {
|
|
56
|
+
const led = JSON.parse(raw);
|
|
57
|
+
if (Array.isArray(led.sessions) && led.sessions.length > 50) {
|
|
58
|
+
led.sessions = led.sessions.slice(-50);
|
|
59
|
+
led._trimmed_for_dashboard = true;
|
|
60
|
+
}
|
|
61
|
+
return JSON.stringify(led);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return raw;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return raw;
|
|
68
|
+
}
|
|
69
|
+
// Express server
|
|
70
|
+
const app = express();
|
|
71
|
+
app.use(express.json());
|
|
72
|
+
// Serve dashboard static files (unauthenticated so the page can load and read its token).
|
|
73
|
+
// In dist: dist/src/daemon/wolf-daemon.js → ../../../dist/dashboard/
|
|
74
|
+
const dashboardDir = path.resolve(__dirname, "..", "..", "..", "dist", "dashboard");
|
|
75
|
+
if (fs.existsSync(dashboardDir)) {
|
|
76
|
+
app.use(express.static(dashboardDir));
|
|
77
|
+
}
|
|
78
|
+
// Require the dashboard token on every /api/* call (header or ?token=). Blocks drive-by
|
|
79
|
+
// requests from a page in the user's browser or another local user (upstream #30/#34).
|
|
80
|
+
app.use("/api", (req, res, next) => {
|
|
81
|
+
const provided = req.headers["x-openwolf-token"] || req.query.token || "";
|
|
82
|
+
if (!tokenMatches(provided, dashboardToken)) {
|
|
83
|
+
res.status(401).json({ error: "unauthorized" });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
next();
|
|
87
|
+
});
|
|
88
|
+
// Detect project metadata
|
|
89
|
+
function detectProjectMeta() {
|
|
90
|
+
let name = path.basename(projectRoot);
|
|
91
|
+
let description = "";
|
|
92
|
+
// Try package.json
|
|
93
|
+
try {
|
|
94
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf-8"));
|
|
95
|
+
if (pkg.name)
|
|
96
|
+
name = pkg.name;
|
|
97
|
+
if (pkg.description)
|
|
98
|
+
description = pkg.description;
|
|
99
|
+
}
|
|
100
|
+
catch { }
|
|
101
|
+
// Try Cargo.toml for name if not found
|
|
102
|
+
if (name === path.basename(projectRoot)) {
|
|
103
|
+
try {
|
|
104
|
+
const cargo = fs.readFileSync(path.join(projectRoot, "Cargo.toml"), "utf-8");
|
|
105
|
+
const nameMatch = cargo.match(/^name\s*=\s*"([^"]+)"/m);
|
|
106
|
+
if (nameMatch)
|
|
107
|
+
name = nameMatch[1];
|
|
108
|
+
}
|
|
109
|
+
catch { }
|
|
110
|
+
}
|
|
111
|
+
// If no description, try cerebrum.md project description
|
|
112
|
+
if (!description) {
|
|
113
|
+
try {
|
|
114
|
+
const cerebrum = fs.readFileSync(path.join(wolfDir, "cerebrum.md"), "utf-8");
|
|
115
|
+
const descMatch = cerebrum.match(/\*\*Project:\*\*\s*(.+)/);
|
|
116
|
+
if (descMatch)
|
|
117
|
+
description = descMatch[1].trim();
|
|
118
|
+
}
|
|
119
|
+
catch { }
|
|
120
|
+
}
|
|
121
|
+
// If still no description, try README first paragraph
|
|
122
|
+
if (!description) {
|
|
123
|
+
for (const readme of ["README.md", "readme.md", "README.rst"]) {
|
|
124
|
+
try {
|
|
125
|
+
const content = fs.readFileSync(path.join(projectRoot, readme), "utf-8");
|
|
126
|
+
const lines = content.split("\n");
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
if (trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("!") && !trimmed.startsWith("=") && !trimmed.startsWith("-") && !trimmed.startsWith("<") && !trimmed.startsWith("[") && !trimmed.startsWith("```") && trimmed.length > 10) {
|
|
130
|
+
description = trimmed.length > 200 ? trimmed.slice(0, 200) + "…" : trimmed;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (description)
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
catch { }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { name, description };
|
|
141
|
+
}
|
|
142
|
+
let projectMeta = detectProjectMeta();
|
|
143
|
+
// API routes
|
|
144
|
+
app.get("/api/config", (_req, res) => {
|
|
145
|
+
res.json({ hasApiKey: !!process.env.ANTHROPIC_API_KEY });
|
|
146
|
+
});
|
|
147
|
+
app.get("/api/projects", (_req, res) => {
|
|
148
|
+
res.json(getRegisteredProjects(true));
|
|
149
|
+
});
|
|
150
|
+
app.post("/api/switch", (req, res) => {
|
|
151
|
+
const { root } = (req.body ?? {});
|
|
152
|
+
if (!root || !fs.existsSync(path.join(root, ".wolf"))) {
|
|
153
|
+
res.status(400).json({ error: "Invalid project root" });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Only switch to a project that's actually registered — don't let an authenticated request
|
|
157
|
+
// point the daemon at an arbitrary directory on disk that happens to contain a .wolf/.
|
|
158
|
+
const isRegistered = getRegisteredProjects(true).some((p) => path.resolve(p.root) === path.resolve(root));
|
|
159
|
+
if (!isRegistered) {
|
|
160
|
+
res.status(403).json({ error: "Not a registered OpenWolf project" });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (root === projectRoot) {
|
|
164
|
+
res.status(400).json({ error: "Already on this project" });
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
res.json({ ok: true });
|
|
168
|
+
// Hot-reload in place — no process restart, existing (authenticated) WS clients are kept.
|
|
169
|
+
setImmediate(() => switchProject(root));
|
|
170
|
+
});
|
|
171
|
+
app.get("/api/health", (_req, res) => {
|
|
172
|
+
const cronState = readJSON(path.join(wolfDir, "cron-state.json"), { engine_status: "unknown", last_heartbeat: null, dead_letter_queue: [] });
|
|
173
|
+
const cronManifest = readJSON(path.join(wolfDir, "cron-manifest.json"), { tasks: [] });
|
|
174
|
+
const taskCount = Array.isArray(cronManifest.tasks) ? cronManifest.tasks.length : 0;
|
|
175
|
+
res.json({
|
|
176
|
+
status: "healthy",
|
|
177
|
+
uptime_seconds: Math.floor((Date.now() - startTime) / 1000),
|
|
178
|
+
last_heartbeat: cronState.last_heartbeat,
|
|
179
|
+
tasks: taskCount,
|
|
180
|
+
dead_letters: Array.isArray(cronState.dead_letter_queue) ? cronState.dead_letter_queue.length : 0,
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
app.get("/api/project", (_req, res) => {
|
|
184
|
+
res.json({
|
|
185
|
+
name: projectMeta.name,
|
|
186
|
+
description: projectMeta.description,
|
|
187
|
+
root: projectRoot,
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
app.get("/api/files", (_req, res) => {
|
|
191
|
+
const files = {};
|
|
192
|
+
for (const file of WOLF_BROADCAST_FILES) {
|
|
193
|
+
files[file] = readWolfFileForDashboard(file);
|
|
194
|
+
}
|
|
195
|
+
// Also try suggestions.json
|
|
196
|
+
try {
|
|
197
|
+
files["suggestions.json"] = fs.readFileSync(path.join(wolfDir, "suggestions.json"), "utf-8");
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
files["suggestions.json"] = "";
|
|
201
|
+
}
|
|
202
|
+
res.json(files);
|
|
203
|
+
});
|
|
204
|
+
app.get("/api/designqc-report", (_req, res) => {
|
|
205
|
+
const report = readJSON(path.join(wolfDir, "designqc-report.json"), null);
|
|
206
|
+
res.json(report);
|
|
207
|
+
});
|
|
208
|
+
// Serve a Design QC capture image so the dashboard can show thumbnails/lightbox
|
|
209
|
+
// instead of bare filenames. Under /api → already behind the dashboard-token gate
|
|
210
|
+
// (which also accepts ?token=, so an <img src> can authenticate). Path-safe.
|
|
211
|
+
app.get("/api/designqc/capture/:file", (req, res) => {
|
|
212
|
+
const capturesDir = path.join(wolfDir, "designqc-captures");
|
|
213
|
+
const safe = path.basename(req.params.file); // strip any traversal
|
|
214
|
+
const filePath = path.join(capturesDir, safe);
|
|
215
|
+
if (!filePath.startsWith(capturesDir + path.sep) || !fs.existsSync(filePath)) {
|
|
216
|
+
res.status(404).json({ error: "capture not found" });
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const ext = path.extname(safe).toLowerCase();
|
|
220
|
+
const type = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
221
|
+
res.setHeader("Content-Type", type);
|
|
222
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
223
|
+
// The capture lives under .wolf/ (a dotdir); Express 5's sendFile defaults to
|
|
224
|
+
// dotfiles:"ignore" and would 404 the whole path. Allow it explicitly.
|
|
225
|
+
res.sendFile(filePath, { dotfiles: "allow" });
|
|
226
|
+
});
|
|
227
|
+
// Manually trigger a Design QC capture (works on a deployed URL, not just localhost — #4, bug 4).
|
|
228
|
+
app.post("/api/designqc/run", (req, res) => {
|
|
229
|
+
const dc = config.openwolf.designqc ?? {};
|
|
230
|
+
const engine = new DesignQCEngine(wolfDir, projectRoot, {
|
|
231
|
+
devServerUrl: req.body?.url || undefined,
|
|
232
|
+
viewports: dc.viewports || DEFAULT_VIEWPORTS,
|
|
233
|
+
maxScreenshots: dc.max_screenshots || 16,
|
|
234
|
+
chromePath: dc.chrome_path ?? undefined,
|
|
235
|
+
quality: 70,
|
|
236
|
+
maxWidth: 1200,
|
|
237
|
+
});
|
|
238
|
+
res.setTimeout(120_000);
|
|
239
|
+
engine.capture()
|
|
240
|
+
.then((result) => res.json({ status: "ok", screenshots: result.screenshots.length, total_size_kb: result.totalSizeKB }))
|
|
241
|
+
.catch((err) => {
|
|
242
|
+
logger.error(`DesignQC run failed: ${err}`);
|
|
243
|
+
res.status(500).json({ error: String(err) });
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
// Trigger a cron task by ID
|
|
247
|
+
app.post("/api/cron/run/:taskId", (req, res) => {
|
|
248
|
+
const { taskId } = req.params;
|
|
249
|
+
if (!cronEngine) {
|
|
250
|
+
res.status(503).json({ error: "Cron engine not running" });
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// Return 202 immediately — the task runs in the background; results arrive via the
|
|
254
|
+
// WebSocket / file-watcher. A failure is broadcast as task_error (upstream #4, bug 1).
|
|
255
|
+
res.status(202).json({ status: "accepted", task_id: taskId });
|
|
256
|
+
cronEngine.runTask(taskId).catch((err) => {
|
|
257
|
+
logger.error(`Manual task trigger failed for ${taskId}: ${err}`);
|
|
258
|
+
broadcast({ type: "task_error", task_id: taskId, error: String(err) });
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
// SPA fallback
|
|
262
|
+
app.get("/{*path}", (_req, res) => {
|
|
263
|
+
const indexPath = path.join(dashboardDir, "index.html");
|
|
264
|
+
if (fs.existsSync(indexPath)) {
|
|
265
|
+
res.sendFile(indexPath);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
res.status(404).json({ error: "Dashboard not built. Run: pnpm build:dashboard" });
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
// Start HTTP server
|
|
272
|
+
const port = config.openwolf.dashboard.port;
|
|
273
|
+
// Bind to loopback by default so the dashboard isn't exposed on the network (upstream #30).
|
|
274
|
+
const host = config.openwolf.dashboard.host || "127.0.0.1";
|
|
275
|
+
const server = app.listen(port, host, () => {
|
|
276
|
+
logger.info(`Dashboard server listening on ${host}:${port}`);
|
|
277
|
+
});
|
|
278
|
+
// WebSocket server. Reject the upgrade before it completes if the token is missing/wrong,
|
|
279
|
+
// so an unauthenticated client never establishes a connection at all.
|
|
280
|
+
const wss = new WebSocketServer({
|
|
281
|
+
server,
|
|
282
|
+
verifyClient: (info, cb) => {
|
|
283
|
+
let t = "";
|
|
284
|
+
try {
|
|
285
|
+
t = new URL(info.req.url || "", "http://localhost").searchParams.get("token") || "";
|
|
286
|
+
}
|
|
287
|
+
catch { /* reject */ }
|
|
288
|
+
if (tokenMatches(t, dashboardToken))
|
|
289
|
+
cb(true);
|
|
290
|
+
else
|
|
291
|
+
cb(false, 401, "unauthorized");
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
wss.on("connection", (ws) => {
|
|
295
|
+
wsClients.add(ws);
|
|
296
|
+
logger.info("WebSocket client connected");
|
|
297
|
+
ws.on("message", (data) => {
|
|
298
|
+
try {
|
|
299
|
+
const msg = JSON.parse(data.toString());
|
|
300
|
+
handleDashboardCommand(msg);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
logger.warn("Invalid WebSocket message received");
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
ws.on("close", () => {
|
|
307
|
+
wsClients.delete(ws);
|
|
308
|
+
});
|
|
309
|
+
// Send initial state
|
|
310
|
+
broadcast({ type: "daemon_started", timestamp: new Date().toISOString() });
|
|
311
|
+
});
|
|
312
|
+
function broadcast(msg) {
|
|
313
|
+
const data = JSON.stringify(msg);
|
|
314
|
+
for (const client of wsClients) {
|
|
315
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
316
|
+
client.send(data);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function handleDashboardCommand(msg) {
|
|
321
|
+
switch (msg.type) {
|
|
322
|
+
case "trigger_task":
|
|
323
|
+
if (msg.task_id && cronEngine) {
|
|
324
|
+
cronEngine.runTask(msg.task_id).catch((err) => {
|
|
325
|
+
logger.error(`Manual task trigger failed: ${err}`);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
case "retry_dead_letter":
|
|
330
|
+
if (msg.task_id) {
|
|
331
|
+
const statePath = path.join(wolfDir, "cron-state.json");
|
|
332
|
+
const state = readJSON(statePath, {
|
|
333
|
+
dead_letter_queue: [],
|
|
334
|
+
});
|
|
335
|
+
state.dead_letter_queue = state.dead_letter_queue.filter((d) => d.task_id !== msg.task_id);
|
|
336
|
+
writeJSON(statePath, state);
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
case "force_scan":
|
|
340
|
+
if (cronEngine) {
|
|
341
|
+
cronEngine.runTask("anatomy-rescan").catch((err) => {
|
|
342
|
+
logger.error(`Force scan failed: ${err}`);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
case "request_full_state":
|
|
347
|
+
// Send all files
|
|
348
|
+
try {
|
|
349
|
+
const files = {};
|
|
350
|
+
for (const file of WOLF_BROADCAST_FILES) {
|
|
351
|
+
files[file] = readWolfFileForDashboard(file);
|
|
352
|
+
}
|
|
353
|
+
broadcast({ type: "full_state", files, timestamp: new Date().toISOString() });
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
logger.error(`Full state request failed: ${err}`);
|
|
357
|
+
}
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Cron engine
|
|
362
|
+
let cronEngine = null;
|
|
363
|
+
if (config.openwolf.cron.enabled) {
|
|
364
|
+
cronEngine = new CronEngine(wolfDir, projectRoot, logger, broadcast);
|
|
365
|
+
cronEngine.start();
|
|
366
|
+
}
|
|
367
|
+
// File watcher
|
|
368
|
+
let fileWatcher = startFileWatcher(wolfDir, logger, broadcast);
|
|
369
|
+
// Hot-switch to another registered project without restarting the process (#4, bug 7).
|
|
370
|
+
// The dashboard token is unchanged, so already-connected clients stay authenticated.
|
|
371
|
+
function switchProject(newRoot) {
|
|
372
|
+
logger.info(`Switching project to: ${newRoot}`);
|
|
373
|
+
// Stop the current subsystems.
|
|
374
|
+
if (cronEngine) {
|
|
375
|
+
cronEngine.stop();
|
|
376
|
+
cronEngine = null;
|
|
377
|
+
}
|
|
378
|
+
fileWatcher.close();
|
|
379
|
+
// Swap the mutable project bindings.
|
|
380
|
+
projectRoot = newRoot;
|
|
381
|
+
wolfDir = path.join(newRoot, ".wolf");
|
|
382
|
+
projectMeta = detectProjectMeta();
|
|
383
|
+
// Restart subsystems for the new project.
|
|
384
|
+
if (config.openwolf.cron.enabled) {
|
|
385
|
+
cronEngine = new CronEngine(wolfDir, projectRoot, logger, broadcast);
|
|
386
|
+
cronEngine.start();
|
|
387
|
+
}
|
|
388
|
+
fileWatcher = startFileWatcher(wolfDir, logger, broadcast);
|
|
389
|
+
// Mark the new project as running.
|
|
390
|
+
const statePath = path.join(wolfDir, "cron-state.json");
|
|
391
|
+
const state = readJSON(statePath, {});
|
|
392
|
+
state.engine_status = "running";
|
|
393
|
+
state.last_heartbeat = new Date().toISOString();
|
|
394
|
+
writeJSON(statePath, state);
|
|
395
|
+
// Push the new project's state to all connected dashboard clients (trimmed via the helper).
|
|
396
|
+
const files = {};
|
|
397
|
+
for (const file of WOLF_BROADCAST_FILES)
|
|
398
|
+
files[file] = readWolfFileForDashboard(file);
|
|
399
|
+
broadcast({ type: "project_switched", project: { name: projectMeta.name, root: projectRoot }, files });
|
|
400
|
+
}
|
|
401
|
+
// Health heartbeat
|
|
402
|
+
const heartbeatInterval = config.openwolf.cron.heartbeat_interval_minutes * 60 * 1000;
|
|
403
|
+
const heartbeatTimer = setInterval(() => {
|
|
404
|
+
const statePath = path.join(wolfDir, "cron-state.json");
|
|
405
|
+
const state = readJSON(statePath, {});
|
|
406
|
+
state.last_heartbeat = new Date().toISOString();
|
|
407
|
+
writeJSON(statePath, state);
|
|
408
|
+
broadcast({ type: "health", status: "healthy", uptime: Math.floor((Date.now() - startTime) / 1000) });
|
|
409
|
+
}, heartbeatInterval);
|
|
410
|
+
// Update cron-state to running
|
|
411
|
+
const cronStatePath = path.join(wolfDir, "cron-state.json");
|
|
412
|
+
const cronState = readJSON(cronStatePath, {});
|
|
413
|
+
cronState.engine_status = "running";
|
|
414
|
+
cronState.last_heartbeat = new Date().toISOString();
|
|
415
|
+
writeJSON(cronStatePath, cronState);
|
|
416
|
+
logger.info("OpenWolf daemon started");
|
|
417
|
+
// Graceful shutdown
|
|
418
|
+
function shutdown() {
|
|
419
|
+
logger.info("Daemon shutting down...");
|
|
420
|
+
broadcast({ type: "daemon_stopping", timestamp: new Date().toISOString() });
|
|
421
|
+
clearInterval(heartbeatTimer);
|
|
422
|
+
if (cronEngine)
|
|
423
|
+
cronEngine.stop();
|
|
424
|
+
const state = readJSON(cronStatePath, {});
|
|
425
|
+
state.engine_status = "stopped";
|
|
426
|
+
writeJSON(cronStatePath, state);
|
|
427
|
+
for (const client of wsClients) {
|
|
428
|
+
client.close();
|
|
429
|
+
}
|
|
430
|
+
wsClients.clear();
|
|
431
|
+
server.close(() => {
|
|
432
|
+
logger.info("Daemon stopped");
|
|
433
|
+
process.exit(0);
|
|
434
|
+
});
|
|
435
|
+
// Force exit after 5s
|
|
436
|
+
setTimeout(() => process.exit(0), 5000);
|
|
437
|
+
}
|
|
438
|
+
process.on("SIGTERM", shutdown);
|
|
439
|
+
process.on("SIGINT", shutdown);
|
|
440
|
+
//# sourceMappingURL=wolf-daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wolf-daemon.js","sourceRoot":"","sources":["../../../src/daemon/wolf-daemon.ts"],"names":[],"mappings":"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"}
|