openwolf 1.0.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/LICENSE +663 -0
- package/README.md +232 -0
- package/dist/bin/openwolf.js +10 -0
- package/dist/bin/openwolf.js.map +1 -0
- package/dist/dashboard/assets/AISuggestions-DzE-DQkR.js +1 -0
- package/dist/dashboard/assets/ActivityTimeline-DGVjujnt.js +1 -0
- package/dist/dashboard/assets/AnatomyBrowser-S-2rmYtw.js +1 -0
- package/dist/dashboard/assets/BugLog-CG2zDHJc.js +1 -0
- package/dist/dashboard/assets/CerebrumViewer-Dlgoy69U.js +1 -0
- package/dist/dashboard/assets/CronStatus-DxUF1iW_.js +1 -0
- package/dist/dashboard/assets/DesignQC-BGXn_aq8.js +1 -0
- package/dist/dashboard/assets/MemoryViewer-CGqkTyvQ.js +1 -0
- package/dist/dashboard/assets/ProjectOverview-DlFhu69i.js +1 -0
- package/dist/dashboard/assets/TokenUsage-DDsQiVIq.js +68 -0
- package/dist/dashboard/assets/index-CzK9GUjV.css +1 -0
- package/dist/dashboard/assets/index-PYeNGjkN.js +52 -0
- package/dist/dashboard/index.html +16 -0
- package/dist/hooks/post-read.js +68 -0
- package/dist/hooks/post-write.js +502 -0
- package/dist/hooks/pre-read.js +79 -0
- package/dist/hooks/pre-write.js +120 -0
- package/dist/hooks/session-start.js +76 -0
- package/dist/hooks/shared.js +613 -0
- package/dist/hooks/stop.js +146 -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 +81 -0
- package/dist/src/buglog/bug-tracker.js.map +1 -0
- package/dist/src/cli/bug-cmd.js +28 -0
- package/dist/src/cli/bug-cmd.js.map +1 -0
- package/dist/src/cli/cron-cmd.js +106 -0
- package/dist/src/cli/cron-cmd.js.map +1 -0
- package/dist/src/cli/daemon-cmd.js +177 -0
- package/dist/src/cli/daemon-cmd.js.map +1 -0
- package/dist/src/cli/dashboard.js +84 -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/index.js +149 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/init.js +506 -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 +85 -0
- package/dist/src/cli/status.js.map +1 -0
- package/dist/src/cli/update.js +414 -0
- package/dist/src/cli/update.js.map +1 -0
- package/dist/src/daemon/cron-engine.js +300 -0
- package/dist/src/daemon/cron-engine.js.map +1 -0
- package/dist/src/daemon/file-watcher.js +53 -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 +294 -0
- package/dist/src/daemon/wolf-daemon.js.map +1 -0
- package/dist/src/designqc/designqc-capture.js +235 -0
- package/dist/src/designqc/designqc-capture.js.map +1 -0
- package/dist/src/designqc/designqc-engine.js +141 -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 +69 -0
- package/dist/src/hooks/post-read.js.map +1 -0
- package/dist/src/hooks/post-write.js +503 -0
- package/dist/src/hooks/post-write.js.map +1 -0
- package/dist/src/hooks/pre-read.js +80 -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 +614 -0
- package/dist/src/hooks/shared.js.map +1 -0
- package/dist/src/hooks/stop.js +147 -0
- package/dist/src/hooks/stop.js.map +1 -0
- package/dist/src/scanner/anatomy-scanner.js +260 -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 +42 -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 +45 -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/fs-safe.js +74 -0
- package/dist/src/utils/fs-safe.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/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/package.json +77 -0
- package/src/templates/OPENWOLF.md +135 -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 +15 -0
- package/src/templates/config.json +73 -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,146 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { getWolfDir, ensureWolfDir, readJSON, writeJSON, appendMarkdown, timeShort } from "./shared.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
ensureWolfDir();
|
|
6
|
+
const wolfDir = getWolfDir();
|
|
7
|
+
const hooksDir = path.join(wolfDir, "hooks");
|
|
8
|
+
const sessionFile = path.join(hooksDir, "_session.json");
|
|
9
|
+
const session = readJSON(sessionFile, {
|
|
10
|
+
session_id: "",
|
|
11
|
+
started: "",
|
|
12
|
+
files_read: {},
|
|
13
|
+
files_written: [],
|
|
14
|
+
edit_counts: {},
|
|
15
|
+
anatomy_hits: 0,
|
|
16
|
+
anatomy_misses: 0,
|
|
17
|
+
repeated_reads_warned: 0,
|
|
18
|
+
cerebrum_warnings: 0,
|
|
19
|
+
stop_count: 0,
|
|
20
|
+
});
|
|
21
|
+
session.stop_count++;
|
|
22
|
+
// Only write to ledger if there's been activity
|
|
23
|
+
const readCount = Object.keys(session.files_read).length;
|
|
24
|
+
const writeCount = session.files_written.length;
|
|
25
|
+
if (readCount === 0 && writeCount === 0) {
|
|
26
|
+
writeJSON(sessionFile, session);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Check for files edited many times without a buglog entry
|
|
31
|
+
checkForMissingBugLogs(wolfDir, session);
|
|
32
|
+
// Check if cerebrum was updated this session (it should be if there were edits)
|
|
33
|
+
checkCerebrumFreshness(wolfDir, session);
|
|
34
|
+
// Build session entry for ledger
|
|
35
|
+
const reads = Object.entries(session.files_read).map(([file, data]) => ({
|
|
36
|
+
file,
|
|
37
|
+
tokens_estimated: data.tokens,
|
|
38
|
+
was_repeated: data.count > 1,
|
|
39
|
+
anatomy_had_description: false, // simplified
|
|
40
|
+
}));
|
|
41
|
+
const writes = session.files_written.map((w) => ({
|
|
42
|
+
file: w.file,
|
|
43
|
+
tokens_estimated: w.tokens,
|
|
44
|
+
action: w.action,
|
|
45
|
+
}));
|
|
46
|
+
const inputTokens = reads.reduce((sum, r) => sum + r.tokens_estimated, 0);
|
|
47
|
+
const outputTokens = writes.reduce((sum, w) => sum + w.tokens_estimated, 0);
|
|
48
|
+
const sessionEntry = {
|
|
49
|
+
id: session.session_id,
|
|
50
|
+
started: session.started,
|
|
51
|
+
ended: new Date().toISOString(),
|
|
52
|
+
reads,
|
|
53
|
+
writes,
|
|
54
|
+
totals: {
|
|
55
|
+
input_tokens_estimated: inputTokens,
|
|
56
|
+
output_tokens_estimated: outputTokens,
|
|
57
|
+
reads_count: readCount,
|
|
58
|
+
writes_count: writeCount,
|
|
59
|
+
repeated_reads_blocked: session.repeated_reads_warned,
|
|
60
|
+
anatomy_lookups: session.anatomy_hits,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
// Update token-ledger.json
|
|
64
|
+
const ledgerPath = path.join(wolfDir, "token-ledger.json");
|
|
65
|
+
const ledger = readJSON(ledgerPath, {
|
|
66
|
+
version: 1,
|
|
67
|
+
created_at: "",
|
|
68
|
+
lifetime: {
|
|
69
|
+
total_tokens_estimated: 0,
|
|
70
|
+
total_reads: 0,
|
|
71
|
+
total_writes: 0,
|
|
72
|
+
total_sessions: 0,
|
|
73
|
+
anatomy_hits: 0,
|
|
74
|
+
anatomy_misses: 0,
|
|
75
|
+
repeated_reads_blocked: 0,
|
|
76
|
+
estimated_savings_vs_bare_cli: 0,
|
|
77
|
+
},
|
|
78
|
+
sessions: [],
|
|
79
|
+
daemon_usage: [],
|
|
80
|
+
waste_flags: [],
|
|
81
|
+
optimization_report: { last_generated: null, patterns: [] },
|
|
82
|
+
});
|
|
83
|
+
ledger.sessions.push(sessionEntry);
|
|
84
|
+
ledger.lifetime.total_reads += readCount;
|
|
85
|
+
ledger.lifetime.total_writes += writeCount;
|
|
86
|
+
ledger.lifetime.total_tokens_estimated += inputTokens + outputTokens;
|
|
87
|
+
ledger.lifetime.anatomy_hits += session.anatomy_hits;
|
|
88
|
+
ledger.lifetime.anatomy_misses += session.anatomy_misses;
|
|
89
|
+
ledger.lifetime.repeated_reads_blocked += session.repeated_reads_warned;
|
|
90
|
+
// Estimate savings: anatomy hits save ~200 tokens each, repeated reads blocked save their token count
|
|
91
|
+
const savedFromAnatomy = session.anatomy_hits * 200;
|
|
92
|
+
const savedFromRepeats = Object.values(session.files_read)
|
|
93
|
+
.filter((r) => r.count > 1)
|
|
94
|
+
.reduce((sum, r) => sum + r.tokens * (r.count - 1), 0);
|
|
95
|
+
ledger.lifetime.estimated_savings_vs_bare_cli += savedFromAnatomy + savedFromRepeats;
|
|
96
|
+
writeJSON(ledgerPath, ledger);
|
|
97
|
+
// Write a session summary line to memory.md if there was meaningful activity
|
|
98
|
+
if (writeCount > 0) {
|
|
99
|
+
try {
|
|
100
|
+
const uniqueFiles = new Set(session.files_written.map(w => path.basename(w.file)));
|
|
101
|
+
const fileList = [...uniqueFiles].slice(0, 5).join(", ");
|
|
102
|
+
const memoryPath = path.join(wolfDir, "memory.md");
|
|
103
|
+
appendMarkdown(memoryPath, `| ${timeShort()} | Session end: ${writeCount} writes across ${uniqueFiles.size} files (${fileList}) | ${readCount} reads | ~${inputTokens + outputTokens} tok |\n`);
|
|
104
|
+
}
|
|
105
|
+
catch { }
|
|
106
|
+
}
|
|
107
|
+
writeJSON(sessionFile, session);
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Check if files were edited multiple times but buglog.json wasn't updated.
|
|
112
|
+
* Emit a stderr reminder so Claude sees it in the next turn.
|
|
113
|
+
*/
|
|
114
|
+
function checkForMissingBugLogs(wolfDir, session) {
|
|
115
|
+
if (!session.edit_counts)
|
|
116
|
+
return;
|
|
117
|
+
const multiEditFiles = Object.entries(session.edit_counts)
|
|
118
|
+
.filter(([, count]) => count >= 3)
|
|
119
|
+
.map(([file]) => path.basename(file));
|
|
120
|
+
if (multiEditFiles.length === 0)
|
|
121
|
+
return;
|
|
122
|
+
// Check if buglog was written to this session
|
|
123
|
+
const buglogWritten = session.files_written.some(w => w.file.includes("buglog.json"));
|
|
124
|
+
if (!buglogWritten) {
|
|
125
|
+
process.stderr.write(`⚠️ OpenWolf: Files edited 3+ times this session (${multiEditFiles.join(", ")}) but buglog.json was not updated. If you fixed bugs, please log them.\n`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Check if cerebrum.md was updated recently. If it hasn't been updated in
|
|
130
|
+
* a while and there was significant activity, emit a gentle reminder.
|
|
131
|
+
*/
|
|
132
|
+
function checkCerebrumFreshness(wolfDir, session) {
|
|
133
|
+
const cerebrumPath = path.join(wolfDir, "cerebrum.md");
|
|
134
|
+
try {
|
|
135
|
+
const stat = fs.statSync(cerebrumPath);
|
|
136
|
+
const hoursSinceUpdate = (Date.now() - stat.mtimeMs) / (1000 * 60 * 60);
|
|
137
|
+
// If cerebrum hasn't been updated in 24h+ and there were significant writes
|
|
138
|
+
if (hoursSinceUpdate > 24 && session.files_written.length >= 3) {
|
|
139
|
+
process.stderr.write(`💡 OpenWolf: cerebrum.md hasn't been updated in ${Math.floor(hoursSinceUpdate)}h. Did you learn any user preferences, conventions, or gotchas this session? Consider updating .wolf/cerebrum.md.\n`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// cerebrum.md doesn't exist, that's ok
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
main().catch(() => process.exit(0));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bug-matcher.js","sourceRoot":"","sources":["../../../src/buglog/bug-matcher.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { readJSON, writeJSON } from "../utils/fs-safe.js";
|
|
3
|
+
export function getBugLogPath(wolfDir) {
|
|
4
|
+
return path.join(wolfDir, "buglog.json");
|
|
5
|
+
}
|
|
6
|
+
export function readBugLog(wolfDir) {
|
|
7
|
+
return readJSON(getBugLogPath(wolfDir), { version: 1, bugs: [] });
|
|
8
|
+
}
|
|
9
|
+
export function logBug(wolfDir, bug) {
|
|
10
|
+
const bugLog = readBugLog(wolfDir);
|
|
11
|
+
const now = new Date().toISOString();
|
|
12
|
+
// Check for near-duplicate (score > 0.8)
|
|
13
|
+
const similar = findSimilarBugs(wolfDir, bug.error_message);
|
|
14
|
+
if (similar.length > 0 && similar[0].score > 0.8) {
|
|
15
|
+
const existing = bugLog.bugs.find((b) => b.id === similar[0].bug.id);
|
|
16
|
+
if (existing) {
|
|
17
|
+
existing.occurrences++;
|
|
18
|
+
existing.last_seen = now;
|
|
19
|
+
writeJSON(getBugLogPath(wolfDir), bugLog);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const id = `bug-${String(bugLog.bugs.length + 1).padStart(3, "0")}`;
|
|
24
|
+
bugLog.bugs.push({
|
|
25
|
+
id,
|
|
26
|
+
timestamp: now,
|
|
27
|
+
error_message: bug.error_message,
|
|
28
|
+
file: bug.file,
|
|
29
|
+
line: bug.line,
|
|
30
|
+
root_cause: bug.root_cause,
|
|
31
|
+
fix: bug.fix,
|
|
32
|
+
tags: bug.tags,
|
|
33
|
+
related_bugs: [],
|
|
34
|
+
occurrences: 1,
|
|
35
|
+
last_seen: now,
|
|
36
|
+
});
|
|
37
|
+
writeJSON(getBugLogPath(wolfDir), bugLog);
|
|
38
|
+
}
|
|
39
|
+
function normalize(text) {
|
|
40
|
+
return text.toLowerCase().replace(/\d+/g, "N").replace(/[^\w\s]/g, " ").trim();
|
|
41
|
+
}
|
|
42
|
+
function tokenize(text) {
|
|
43
|
+
return new Set(normalize(text).split(/\s+/).filter((w) => w.length > 2));
|
|
44
|
+
}
|
|
45
|
+
function jaccardSimilarity(a, b) {
|
|
46
|
+
const intersection = new Set([...a].filter((x) => b.has(x)));
|
|
47
|
+
const union = new Set([...a, ...b]);
|
|
48
|
+
return union.size === 0 ? 0 : intersection.size / union.size;
|
|
49
|
+
}
|
|
50
|
+
export function findSimilarBugs(wolfDir, errorMessage) {
|
|
51
|
+
const bugLog = readBugLog(wolfDir);
|
|
52
|
+
const normalizedInput = normalize(errorMessage);
|
|
53
|
+
const inputTokens = tokenize(errorMessage);
|
|
54
|
+
const results = [];
|
|
55
|
+
for (const bug of bugLog.bugs) {
|
|
56
|
+
let score = 0;
|
|
57
|
+
// Exact substring match
|
|
58
|
+
if (normalize(bug.error_message).includes(normalizedInput) ||
|
|
59
|
+
normalizedInput.includes(normalize(bug.error_message))) {
|
|
60
|
+
score += 1.0;
|
|
61
|
+
}
|
|
62
|
+
// Word overlap (jaccard)
|
|
63
|
+
const bugTokens = tokenize(bug.error_message);
|
|
64
|
+
score += jaccardSimilarity(inputTokens, bugTokens) * 0.5;
|
|
65
|
+
if (score > 0.3) {
|
|
66
|
+
results.push({ bug, score });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
results.sort((a, b) => b.score - a.score);
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
export function searchBugs(wolfDir, term) {
|
|
73
|
+
const bugLog = readBugLog(wolfDir);
|
|
74
|
+
const lower = term.toLowerCase();
|
|
75
|
+
return bugLog.bugs.filter((b) => b.error_message.toLowerCase().includes(lower) ||
|
|
76
|
+
b.root_cause.toLowerCase().includes(lower) ||
|
|
77
|
+
b.fix.toLowerCase().includes(lower) ||
|
|
78
|
+
b.tags.some((t) => t.toLowerCase().includes(lower)) ||
|
|
79
|
+
b.file.toLowerCase().includes(lower));
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=bug-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bug-tracker.js","sourceRoot":"","sources":["../../../src/buglog/bug-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { findProjectRoot } from "../scanner/project-root.js";
|
|
4
|
+
import { searchBugs } from "../buglog/bug-tracker.js";
|
|
5
|
+
export function bugSearch(term) {
|
|
6
|
+
const projectRoot = findProjectRoot();
|
|
7
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
8
|
+
if (!fs.existsSync(wolfDir)) {
|
|
9
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const results = searchBugs(wolfDir, term);
|
|
13
|
+
if (results.length === 0) {
|
|
14
|
+
console.log(`No bugs found matching "${term}".`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
console.log(`Found ${results.length} matching bug(s):\n`);
|
|
18
|
+
for (const bug of results) {
|
|
19
|
+
console.log(` [${bug.id}] ${bug.error_message.slice(0, 80)}`);
|
|
20
|
+
console.log(` File: ${bug.file}${bug.line ? `:${bug.line}` : ""}`);
|
|
21
|
+
console.log(` Root cause: ${bug.root_cause}`);
|
|
22
|
+
console.log(` Fix: ${bug.fix}`);
|
|
23
|
+
console.log(` Tags: ${bug.tags.join(", ")}`);
|
|
24
|
+
console.log(` Occurrences: ${bug.occurrences} | Last seen: ${bug.last_seen}`);
|
|
25
|
+
console.log("");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=bug-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bug-cmd.js","sourceRoot":"","sources":["../../../src/cli/bug-cmd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,WAAW,GAAG,eAAe,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAEhD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAC5D,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAE1C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,IAAI,CAAC,CAAC;QACjD,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,MAAM,qBAAqB,CAAC,CAAC;IAE1D,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,CAAC,WAAW,iBAAiB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QACjF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { findProjectRoot } from "../scanner/project-root.js";
|
|
4
|
+
import { readJSON, writeJSON } from "../utils/fs-safe.js";
|
|
5
|
+
import { Logger } from "../utils/logger.js";
|
|
6
|
+
import { CronEngine } from "../daemon/cron-engine.js";
|
|
7
|
+
export function cronList() {
|
|
8
|
+
const projectRoot = findProjectRoot();
|
|
9
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
10
|
+
if (!fs.existsSync(wolfDir)) {
|
|
11
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const manifest = readJSON(path.join(wolfDir, "cron-manifest.json"), {
|
|
15
|
+
version: 1,
|
|
16
|
+
tasks: [],
|
|
17
|
+
});
|
|
18
|
+
const state = readJSON(path.join(wolfDir, "cron-state.json"), {
|
|
19
|
+
engine_status: "unknown",
|
|
20
|
+
execution_log: [],
|
|
21
|
+
dead_letter_queue: [],
|
|
22
|
+
});
|
|
23
|
+
console.log("Cron Tasks");
|
|
24
|
+
console.log("==========\n");
|
|
25
|
+
if (manifest.tasks.length === 0) {
|
|
26
|
+
console.log(" No tasks configured.");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const task of manifest.tasks) {
|
|
30
|
+
const status = task.enabled ? "enabled" : "disabled";
|
|
31
|
+
const lastRun = state.execution_log
|
|
32
|
+
.filter((e) => e.task_id === task.id)
|
|
33
|
+
.sort((a, b) => b.timestamp.localeCompare(a.timestamp))[0];
|
|
34
|
+
const lastRunStr = lastRun ? `${lastRun.status} at ${lastRun.timestamp}` : "never";
|
|
35
|
+
const isDead = state.dead_letter_queue.some((d) => d.task_id === task.id);
|
|
36
|
+
console.log(` ${task.name} (${task.id})`);
|
|
37
|
+
console.log(` Schedule: ${task.schedule}`);
|
|
38
|
+
console.log(` Status: ${status}${isDead ? " [DEAD-LETTERED]" : ""}`);
|
|
39
|
+
console.log(` Last run: ${lastRunStr}`);
|
|
40
|
+
console.log(` ${task.description}`);
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function cronRun(id) {
|
|
45
|
+
const projectRoot = findProjectRoot();
|
|
46
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
47
|
+
if (!fs.existsSync(wolfDir)) {
|
|
48
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const config = readJSON(path.join(wolfDir, "config.json"), {
|
|
52
|
+
openwolf: { dashboard: { port: 18791 } },
|
|
53
|
+
});
|
|
54
|
+
const port = config.openwolf.dashboard.port;
|
|
55
|
+
// Try calling the daemon's HTTP endpoint first
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/cron/run/${encodeURIComponent(id)}`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: { "Content-Type": "application/json" },
|
|
60
|
+
});
|
|
61
|
+
const body = await res.json();
|
|
62
|
+
if (res.ok) {
|
|
63
|
+
console.log(`Task ${id} triggered via daemon.`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log(`Daemon returned error: ${body.error ?? res.statusText}`);
|
|
67
|
+
console.log("Falling back to direct execution...");
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
console.log("Daemon not reachable. Running task directly...");
|
|
71
|
+
}
|
|
72
|
+
// Fallback: run the task directly via CronEngine
|
|
73
|
+
const logger = new Logger(path.join(wolfDir, "daemon.log"), "info");
|
|
74
|
+
const engine = new CronEngine(wolfDir, projectRoot, logger, () => { });
|
|
75
|
+
try {
|
|
76
|
+
await engine.runTask(id);
|
|
77
|
+
console.log(`Task ${id} executed successfully.`);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
console.error(`Task ${id} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function cronRetry(id) {
|
|
85
|
+
const projectRoot = findProjectRoot();
|
|
86
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
87
|
+
if (!fs.existsSync(wolfDir)) {
|
|
88
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const statePath = path.join(wolfDir, "cron-state.json");
|
|
92
|
+
const state = readJSON(statePath, {
|
|
93
|
+
engine_status: "unknown",
|
|
94
|
+
execution_log: [],
|
|
95
|
+
dead_letter_queue: [],
|
|
96
|
+
});
|
|
97
|
+
const idx = state.dead_letter_queue.findIndex((d) => d.task_id === id);
|
|
98
|
+
if (idx === -1) {
|
|
99
|
+
console.log(`Task ${id} not found in dead letter queue.`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
state.dead_letter_queue.splice(idx, 1);
|
|
103
|
+
writeJSON(statePath, state);
|
|
104
|
+
console.log(`Removed ${id} from dead letter queue. It will retry on next schedule.`);
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=cron-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-cmd.js","sourceRoot":"","sources":["../../../src/cli/cron-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { findProjectRoot } from "../scanner/project-root.js";
|
|
6
|
+
import { readJSON } from "../utils/fs-safe.js";
|
|
7
|
+
import { isWindows } from "../utils/platform.js";
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
function getDashboardPort() {
|
|
11
|
+
const projectRoot = findProjectRoot();
|
|
12
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
13
|
+
const config = readJSON(path.join(wolfDir, "config.json"), { openwolf: { dashboard: { port: 18791 } } });
|
|
14
|
+
return config.openwolf.dashboard.port;
|
|
15
|
+
}
|
|
16
|
+
function getPm2Name() {
|
|
17
|
+
const projectRoot = findProjectRoot();
|
|
18
|
+
return `openwolf-${path.basename(projectRoot)}`;
|
|
19
|
+
}
|
|
20
|
+
function hasPm2() {
|
|
21
|
+
try {
|
|
22
|
+
const cmd = isWindows() ? "where pm2" : "which pm2";
|
|
23
|
+
execSync(cmd, { stdio: "ignore" });
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function findPidOnPort(port) {
|
|
31
|
+
try {
|
|
32
|
+
if (isWindows()) {
|
|
33
|
+
const output = execSync(`netstat -ano -p tcp`, { encoding: "utf-8" });
|
|
34
|
+
for (const line of output.split("\n")) {
|
|
35
|
+
if (line.includes(`:${port}`) && line.includes("LISTENING")) {
|
|
36
|
+
const parts = line.trim().split(/\s+/);
|
|
37
|
+
const pid = parseInt(parts[parts.length - 1], 10);
|
|
38
|
+
if (pid > 0)
|
|
39
|
+
return pid;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const output = execSync(`lsof -ti :${port}`, { encoding: "utf-8" });
|
|
45
|
+
const pid = parseInt(output.trim(), 10);
|
|
46
|
+
if (pid > 0)
|
|
47
|
+
return pid;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
function killPid(pid) {
|
|
54
|
+
try {
|
|
55
|
+
if (isWindows()) {
|
|
56
|
+
execSync(`taskkill /PID ${pid} /F`, { stdio: "ignore" });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
process.kill(pid, "SIGTERM");
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function daemonStart() {
|
|
68
|
+
const projectRoot = findProjectRoot();
|
|
69
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
70
|
+
if (!fs.existsSync(wolfDir)) {
|
|
71
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (!hasPm2()) {
|
|
75
|
+
console.log("pm2 not found. Install with: pnpm add -g pm2");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const name = getPm2Name();
|
|
79
|
+
// Resolve daemon script relative to openwolf's install dir, not the target project
|
|
80
|
+
const daemonScript = path.resolve(__dirname, "..", "daemon", "wolf-daemon.js");
|
|
81
|
+
try {
|
|
82
|
+
execSync(`pm2 start "${daemonScript}" --name ${name} --cwd "${projectRoot}" -- --env OPENWOLF_PROJECT_ROOT="${projectRoot}"`, {
|
|
83
|
+
stdio: "inherit",
|
|
84
|
+
env: { ...process.env, OPENWOLF_PROJECT_ROOT: projectRoot },
|
|
85
|
+
});
|
|
86
|
+
execSync("pm2 save", { stdio: "ignore" });
|
|
87
|
+
console.log(`\n ✓ Daemon started: ${name}`);
|
|
88
|
+
if (isWindows()) {
|
|
89
|
+
console.log(" Tip: Run 'pm2-windows-startup' for boot persistence.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
console.error("Failed to start daemon.");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export function daemonStop() {
|
|
97
|
+
const projectRoot = findProjectRoot();
|
|
98
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
99
|
+
if (!fs.existsSync(wolfDir)) {
|
|
100
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// First try PM2
|
|
104
|
+
if (hasPm2()) {
|
|
105
|
+
const name = getPm2Name();
|
|
106
|
+
try {
|
|
107
|
+
execSync(`pm2 stop ${name}`, { stdio: "ignore" });
|
|
108
|
+
console.log(` ✓ Daemon stopped (PM2): ${name}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// PM2 process not found — fall through to port-based stop
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Fall back to killing whatever is listening on the dashboard port
|
|
116
|
+
const port = getDashboardPort();
|
|
117
|
+
const pid = findPidOnPort(port);
|
|
118
|
+
if (pid) {
|
|
119
|
+
if (killPid(pid)) {
|
|
120
|
+
console.log(` ✓ Daemon stopped (PID ${pid} on port ${port})`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.error(` Failed to kill process ${pid} on port ${port}.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log(` No daemon running on port ${port}.`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export function daemonRestart() {
|
|
131
|
+
const projectRoot = findProjectRoot();
|
|
132
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
133
|
+
if (!fs.existsSync(wolfDir)) {
|
|
134
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// First try PM2
|
|
138
|
+
if (hasPm2()) {
|
|
139
|
+
const name = getPm2Name();
|
|
140
|
+
try {
|
|
141
|
+
execSync(`pm2 restart ${name}`, { stdio: "ignore" });
|
|
142
|
+
console.log(` ✓ Daemon restarted (PM2): ${name}`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
// PM2 process not found — fall through
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Fall back: stop then start via dashboard command flow
|
|
150
|
+
const port = getDashboardPort();
|
|
151
|
+
const pid = findPidOnPort(port);
|
|
152
|
+
if (pid) {
|
|
153
|
+
killPid(pid);
|
|
154
|
+
console.log(` Stopped old daemon (PID ${pid}).`);
|
|
155
|
+
}
|
|
156
|
+
console.log(" Use 'openwolf dashboard' to start a new daemon.");
|
|
157
|
+
}
|
|
158
|
+
export function daemonLogs() {
|
|
159
|
+
const projectRoot = findProjectRoot();
|
|
160
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
161
|
+
if (!fs.existsSync(wolfDir)) {
|
|
162
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (!hasPm2()) {
|
|
166
|
+
console.log("pm2 not found.");
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const name = getPm2Name();
|
|
170
|
+
try {
|
|
171
|
+
execSync(`pm2 logs ${name} --lines 50 --nostream`, { stdio: "inherit" });
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
console.error("Failed to get daemon logs.");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=daemon-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-cmd.js","sourceRoot":"","sources":["../../../src/cli/daemon-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as net from "node:net";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { fork } from "node:child_process";
|
|
6
|
+
import { findProjectRoot } from "../scanner/project-root.js";
|
|
7
|
+
import { readJSON } from "../utils/fs-safe.js";
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
function isPortOpen(port) {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const socket = new net.Socket();
|
|
13
|
+
socket.setTimeout(1000);
|
|
14
|
+
socket.once("connect", () => {
|
|
15
|
+
socket.destroy();
|
|
16
|
+
resolve(true);
|
|
17
|
+
});
|
|
18
|
+
socket.once("timeout", () => {
|
|
19
|
+
socket.destroy();
|
|
20
|
+
resolve(false);
|
|
21
|
+
});
|
|
22
|
+
socket.once("error", () => {
|
|
23
|
+
socket.destroy();
|
|
24
|
+
resolve(false);
|
|
25
|
+
});
|
|
26
|
+
socket.connect(port, "127.0.0.1");
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export async function dashboardCommand() {
|
|
30
|
+
const projectRoot = findProjectRoot();
|
|
31
|
+
const wolfDir = path.join(projectRoot, ".wolf");
|
|
32
|
+
if (!fs.existsSync(wolfDir)) {
|
|
33
|
+
console.log("OpenWolf not initialized. Run: openwolf init");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const config = readJSON(path.join(wolfDir, "config.json"), {
|
|
37
|
+
openwolf: { dashboard: { port: 18791 } },
|
|
38
|
+
});
|
|
39
|
+
const port = config.openwolf.dashboard.port;
|
|
40
|
+
const url = `http://localhost:${port}`;
|
|
41
|
+
// Check if daemon is already running on that port
|
|
42
|
+
const running = await isPortOpen(port);
|
|
43
|
+
if (!running) {
|
|
44
|
+
console.log(" Daemon not running. Starting dashboard server...");
|
|
45
|
+
// Find the daemon script
|
|
46
|
+
const daemonScript = path.resolve(__dirname, "..", "daemon", "wolf-daemon.js");
|
|
47
|
+
if (!fs.existsSync(daemonScript)) {
|
|
48
|
+
console.error(` Daemon script not found at: ${daemonScript}`);
|
|
49
|
+
console.log(" Run 'pnpm build' in the openwolf directory first.");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Fork the daemon as a child process, passing project root explicitly
|
|
53
|
+
const child = fork(daemonScript, [], {
|
|
54
|
+
cwd: projectRoot,
|
|
55
|
+
env: { ...process.env, OPENWOLF_PROJECT_ROOT: projectRoot },
|
|
56
|
+
detached: true,
|
|
57
|
+
stdio: "ignore",
|
|
58
|
+
});
|
|
59
|
+
child.unref();
|
|
60
|
+
// Wait for the port to open (up to 5 seconds)
|
|
61
|
+
let ready = false;
|
|
62
|
+
for (let i = 0; i < 25; i++) {
|
|
63
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
64
|
+
if (await isPortOpen(port)) {
|
|
65
|
+
ready = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!ready) {
|
|
70
|
+
console.log(` Server didn't start in time. Try manually: node "${daemonScript}"`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.log(` ✓ Dashboard server running on port ${port}`);
|
|
74
|
+
}
|
|
75
|
+
console.log(` Opening ${url}...`);
|
|
76
|
+
try {
|
|
77
|
+
const { default: open } = await import("open");
|
|
78
|
+
await open(url);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
console.log(` Could not open browser. Visit: ${url}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=dashboard.js.map
|