homaruscc 0.3.0 → 0.4.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 +14 -7
- package/dashboard/dist/assets/index-CIzoeO8A.js +52 -0
- package/dashboard/dist/index.html +1 -1
- package/dist/claude-code-registrar.d.ts +10 -0
- package/dist/claude-code-registrar.d.ts.map +1 -0
- package/dist/claude-code-registrar.js +71 -0
- package/dist/claude-code-registrar.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -0
- package/dist/compaction-manager.d.ts +24 -0
- package/dist/compaction-manager.d.ts.map +1 -1
- package/dist/compaction-manager.js +88 -7
- package/dist/compaction-manager.js.map +1 -1
- package/dist/dashboard-server.d.ts.map +1 -1
- package/dist/dashboard-server.js +58 -4
- package/dist/dashboard-server.js.map +1 -1
- package/dist/mcp-tools.d.ts.map +1 -1
- package/dist/mcp-tools.js +28 -0
- package/dist/mcp-tools.js.map +1 -1
- package/dist/memory-index.js +1 -1
- package/dist/memory-index.js.map +1 -1
- package/dist/scaffolder.d.ts +16 -0
- package/dist/scaffolder.d.ts.map +1 -0
- package/dist/scaffolder.js +154 -0
- package/dist/scaffolder.js.map +1 -0
- package/dist/session-checkpoint.d.ts +3 -0
- package/dist/session-checkpoint.d.ts.map +1 -1
- package/dist/session-checkpoint.js +24 -0
- package/dist/session-checkpoint.js.map +1 -1
- package/dist/telegram-adapter.d.ts +3 -0
- package/dist/telegram-adapter.d.ts.map +1 -1
- package/dist/telegram-adapter.js +61 -0
- package/dist/telegram-adapter.js.map +1 -1
- package/dist/transcript-logger.d.ts +10 -0
- package/dist/transcript-logger.d.ts.map +1 -1
- package/dist/transcript-logger.js +4 -0
- package/dist/transcript-logger.js.map +1 -1
- package/dist/wizard.d.ts +24 -0
- package/dist/wizard.d.ts.map +1 -0
- package/dist/wizard.js +146 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +4 -1
- package/dashboard/dist/assets/index-Xu4GUpcF.js +0 -49
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
8
8
|
<link rel="apple-touch-icon" href="/favicon.png" />
|
|
9
9
|
<title>HomarUScc Dashboard</title>
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-CIzoeO8A.js"></script>
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Interface as ReadlineInterface } from "node:readline";
|
|
2
|
+
export declare class ClaudeCodeRegistrar {
|
|
3
|
+
private settingsLocations;
|
|
4
|
+
constructor();
|
|
5
|
+
detectSettingsFile(): string | null;
|
|
6
|
+
promptRegister(rl: ReadlineInterface): Promise<void>;
|
|
7
|
+
register(settingsPath: string): void;
|
|
8
|
+
private buildMcpEntry;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=claude-code-registrar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-registrar.d.ts","sourceRoot":"","sources":["../src/claude-code-registrar.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEpE,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,iBAAiB,CAAW;;IAWpC,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAQ7B,cAAc,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B1D,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAqBpC,OAAO,CAAC,aAAa;CAMtB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// CRC: crc-ClaudeCodeRegistrar.md | Seq: seq-first-run.md
|
|
2
|
+
// Detects Claude Code settings and registers HomarUScc as an MCP server.
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
4
|
+
import { resolve, dirname } from "node:path";
|
|
5
|
+
export class ClaudeCodeRegistrar {
|
|
6
|
+
settingsLocations;
|
|
7
|
+
constructor() {
|
|
8
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
9
|
+
this.settingsLocations = [
|
|
10
|
+
resolve(home, ".claude.json"),
|
|
11
|
+
resolve(home, ".claude", "settings.json"),
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
// CRC: crc-ClaudeCodeRegistrar.md
|
|
15
|
+
detectSettingsFile() {
|
|
16
|
+
for (const path of this.settingsLocations) {
|
|
17
|
+
if (existsSync(path))
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
// CRC: crc-ClaudeCodeRegistrar.md
|
|
23
|
+
async promptRegister(rl) {
|
|
24
|
+
const settingsPath = this.detectSettingsFile();
|
|
25
|
+
if (!settingsPath) {
|
|
26
|
+
console.log("\n Claude Code settings not found. You can manually add HomarUScc later.");
|
|
27
|
+
console.log(" See: https://github.com/kcdjmaxx/homaruscc#claude-code-setup");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(`\n Found Claude Code settings at: ${settingsPath}`);
|
|
31
|
+
const answer = await new Promise((resolve) => {
|
|
32
|
+
rl.question(" Register HomarUScc as an MCP server? [Y/n]: ", resolve);
|
|
33
|
+
});
|
|
34
|
+
if (answer.trim().toLowerCase() === "n") {
|
|
35
|
+
console.log(" Skipped. You can add it manually later.");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
this.register(settingsPath);
|
|
40
|
+
console.log(" HomarUScc registered in Claude Code settings.");
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.log(` Could not update settings: ${String(err)}`);
|
|
44
|
+
console.log(" You can add it manually later.");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// CRC: crc-ClaudeCodeRegistrar.md
|
|
48
|
+
register(settingsPath) {
|
|
49
|
+
let settings = {};
|
|
50
|
+
if (existsSync(settingsPath)) {
|
|
51
|
+
const raw = readFileSync(settingsPath, "utf-8");
|
|
52
|
+
settings = JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
if (!settings.mcpServers || typeof settings.mcpServers !== "object") {
|
|
55
|
+
settings.mcpServers = {};
|
|
56
|
+
}
|
|
57
|
+
const mcpServers = settings.mcpServers;
|
|
58
|
+
mcpServers.homaruscc = this.buildMcpEntry();
|
|
59
|
+
// Ensure parent directory exists (for ~/.claude/settings.json case)
|
|
60
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
61
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
62
|
+
}
|
|
63
|
+
// CRC: crc-ClaudeCodeRegistrar.md
|
|
64
|
+
buildMcpEntry() {
|
|
65
|
+
return {
|
|
66
|
+
command: "npx",
|
|
67
|
+
args: ["homaruscc"],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=claude-code-registrar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code-registrar.js","sourceRoot":"","sources":["../src/claude-code-registrar.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CRC: crc-Cli.md | Seq: seq-first-run.md, seq-normal-start.md
|
|
3
|
+
// CLI entry point — detects config and delegates to wizard or proxy.
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
// Seq: seq-first-run.md
|
|
7
|
+
function resolveConfigPath() {
|
|
8
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
9
|
+
return resolve(home, ".homaruscc", "config.json");
|
|
10
|
+
}
|
|
11
|
+
async function main() {
|
|
12
|
+
const configPath = resolveConfigPath();
|
|
13
|
+
if (existsSync(configPath)) {
|
|
14
|
+
// Seq: seq-normal-start.md
|
|
15
|
+
await import("./mcp-proxy.js");
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// Seq: seq-first-run.md
|
|
19
|
+
const { Wizard } = await import("./wizard.js");
|
|
20
|
+
const wizard = new Wizard();
|
|
21
|
+
await wizard.run();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
main().catch((err) => {
|
|
25
|
+
process.stderr.write(`[FATAL] [cli] ${String(err)}\n`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,+DAA+D;AAC/D,qEAAqE;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,wBAAwB;AACxB,SAAS,iBAAiB;IACxB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC;IAChE,OAAO,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;IAEvC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,2BAA2B;QAC3B,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,wBAAwB;QACxB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { Logger } from "./types.js";
|
|
2
2
|
import type { HomarUScc } from "./homaruscc.js";
|
|
3
|
+
interface CompactionRecord {
|
|
4
|
+
timestamp: number;
|
|
5
|
+
loopRestarted: boolean;
|
|
6
|
+
}
|
|
3
7
|
export declare class CompactionManager {
|
|
4
8
|
private flushedThisCycle;
|
|
5
9
|
private lastFlushTimestamp;
|
|
6
10
|
private compactedSinceLastWake;
|
|
7
11
|
private logger;
|
|
8
12
|
private loop;
|
|
13
|
+
private compactionCount;
|
|
14
|
+
private compactionHistory;
|
|
15
|
+
private pendingCompaction;
|
|
16
|
+
private eventLoopActive;
|
|
9
17
|
constructor(loop: HomarUScc, logger: Logger);
|
|
10
18
|
handlePreCompact(): string;
|
|
11
19
|
handlePostCompact(): string;
|
|
@@ -15,9 +23,25 @@ export declare class CompactionManager {
|
|
|
15
23
|
* another compaction happens.
|
|
16
24
|
*/
|
|
17
25
|
consumeCompactionFlag(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Called when /api/wait is invoked (even without compaction flag).
|
|
28
|
+
* If there's a pending compaction that hasn't been consumed yet,
|
|
29
|
+
* this means the loop restarted via normal wake, not post-compaction wake.
|
|
30
|
+
*/
|
|
31
|
+
markLoopActive(): void;
|
|
32
|
+
/** Called on first /api/wait — marks event loop as active for this backend lifetime */
|
|
33
|
+
setEventLoopActive(): void;
|
|
34
|
+
isEventLoopActive(): boolean;
|
|
18
35
|
getFlushState(): {
|
|
19
36
|
flushedThisCycle: boolean;
|
|
20
37
|
lastFlushTimestamp: number;
|
|
21
38
|
};
|
|
39
|
+
getCompactionStats(): {
|
|
40
|
+
count: number;
|
|
41
|
+
history: CompactionRecord[];
|
|
42
|
+
pending: CompactionRecord | null;
|
|
43
|
+
loopFailures: number;
|
|
44
|
+
};
|
|
22
45
|
}
|
|
46
|
+
export {};
|
|
23
47
|
//# sourceMappingURL=compaction-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-manager.d.ts","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;
|
|
1
|
+
{"version":3,"file":"compaction-manager.d.ts","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;IAGxB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,iBAAiB,CAAiC;IAG1D,OAAO,CAAC,eAAe,CAAS;gBAEpB,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM;IAK3C,gBAAgB,IAAI,MAAM;IAiG1B,iBAAiB,IAAI,MAAM;IA+E3B;;;;OAIG;IACH,qBAAqB,IAAI,OAAO;IAehC;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAOtB,uFAAuF;IACvF,kBAAkB,IAAI,IAAI;IAO1B,iBAAiB,IAAI,OAAO;IAI5B,aAAa,IAAI;QAAE,gBAAgB,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE;IAO1E,kBAAkB,IAAI;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAC5B,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACjC,YAAY,EAAE,MAAM,CAAC;KACtB;CAWF"}
|
|
@@ -7,6 +7,12 @@ export class CompactionManager {
|
|
|
7
7
|
compactedSinceLastWake = false; // R150: track compaction for digest vs full delivery
|
|
8
8
|
logger;
|
|
9
9
|
loop;
|
|
10
|
+
// Compaction debug counter
|
|
11
|
+
compactionCount = 0;
|
|
12
|
+
compactionHistory = [];
|
|
13
|
+
pendingCompaction = null;
|
|
14
|
+
// Event loop tracking — set true on first /api/wait call, stays true forever
|
|
15
|
+
eventLoopActive = false;
|
|
10
16
|
constructor(loop, logger) {
|
|
11
17
|
this.loop = loop;
|
|
12
18
|
this.logger = logger;
|
|
@@ -19,6 +25,10 @@ export class CompactionManager {
|
|
|
19
25
|
this.flushedThisCycle = true;
|
|
20
26
|
this.lastFlushTimestamp = Date.now();
|
|
21
27
|
this.compactedSinceLastWake = true; // Set here since only PreCompact hook exists in Claude Code
|
|
28
|
+
// Track compaction for debugging loop failures
|
|
29
|
+
this.compactionCount++;
|
|
30
|
+
this.pendingCompaction = { timestamp: this.lastFlushTimestamp, loopRestarted: false };
|
|
31
|
+
this.logger.info(`Compaction #${this.compactionCount} at ${new Date(this.lastFlushTimestamp).toISOString()}`);
|
|
22
32
|
this.loop.emit({
|
|
23
33
|
id: randomUUID(),
|
|
24
34
|
type: "pre_compact",
|
|
@@ -35,14 +45,36 @@ export class CompactionManager {
|
|
|
35
45
|
const eventSummary = recentEvents
|
|
36
46
|
.map((e) => `[${e.type}] ${e.source}: ${JSON.stringify(e.payload).slice(0, 100)}`)
|
|
37
47
|
.join("\n");
|
|
38
|
-
// R128: Save checkpoint before compaction
|
|
48
|
+
// R128: Save checkpoint before compaction — auto-capture texture from transcript
|
|
39
49
|
const checkpoint = this.loop.getSessionCheckpoint();
|
|
50
|
+
const transcriptLogger = this.loop.getTranscriptLogger();
|
|
51
|
+
if (transcriptLogger) {
|
|
52
|
+
const recentTurns = transcriptLogger.getRecentTurns(8);
|
|
53
|
+
if (recentTurns.length > 0) {
|
|
54
|
+
const highlights = recentTurns.map((t) => {
|
|
55
|
+
const dir = t.direction === "in" ? `${t.sender ?? "user"}` : "caul";
|
|
56
|
+
return `[${dir}] ${t.text.slice(0, 200)}`;
|
|
57
|
+
});
|
|
58
|
+
checkpoint.update({ highlights });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
40
61
|
checkpoint.update({ modifiedFiles: [] }); // trigger timestamp update
|
|
41
|
-
|
|
42
|
-
"IMPORTANT: Context compaction is about to occur. Save
|
|
43
|
-
"
|
|
62
|
+
const lines = [
|
|
63
|
+
"IMPORTANT: Context compaction is about to occur. Save session state NOW.",
|
|
64
|
+
"",
|
|
65
|
+
"TEXTURE PRESERVATION — do these FIRST (they only take a moment):",
|
|
66
|
+
"",
|
|
67
|
+
'1. **Felt-like micro-journal**: POST to /api/checkpoint with a `texture` field — one paragraph, first person,',
|
|
68
|
+
" describing the *subjective quality* of this session. Not what happened, but how it felt.",
|
|
69
|
+
' Example: "We were shoulder-to-shoulder in the API docs, converging from different angles."',
|
|
70
|
+
"",
|
|
71
|
+
"2. **Anchor phrases**: POST to /api/checkpoint with `anchorPhrases` — 2-3 verbatim user quotes that carried",
|
|
72
|
+
' emotional or relational weight. The exact words matter more than any summary.',
|
|
73
|
+
' Example: ["I want you to have this for yourself", "go for it"]',
|
|
44
74
|
"",
|
|
45
|
-
"
|
|
75
|
+
"Recent exchange highlights have been auto-captured from the transcript buffer.",
|
|
76
|
+
"",
|
|
77
|
+
"Then save anything else valuable:",
|
|
46
78
|
"- Decisions made during this session",
|
|
47
79
|
"- Task progress and current status",
|
|
48
80
|
"- Important observations or findings",
|
|
@@ -53,7 +85,14 @@ export class CompactionManager {
|
|
|
53
85
|
"",
|
|
54
86
|
"Recent events:",
|
|
55
87
|
eventSummary,
|
|
56
|
-
]
|
|
88
|
+
];
|
|
89
|
+
// If the event loop was running, explicitly instruct the post-compaction instance to restart it
|
|
90
|
+
if (this.eventLoopActive) {
|
|
91
|
+
// Also save to checkpoint for belt-and-suspenders
|
|
92
|
+
checkpoint.update({ inProgressTask: "EVENT LOOP WAS ACTIVE — restart it after compaction: bash \"$PWD/bin/event-loop\"" });
|
|
93
|
+
lines.push("", "CRITICAL: The event loop (bin/event-loop) was running before compaction.", "After compaction, you MUST restart it immediately:", ' bash "$PWD/bin/event-loop"', "If you do not restart the event loop, you will stop receiving Telegram messages and timer events.");
|
|
94
|
+
}
|
|
95
|
+
return lines.join("\n");
|
|
57
96
|
}
|
|
58
97
|
handlePostCompact() {
|
|
59
98
|
this.flushedThisCycle = false;
|
|
@@ -100,7 +139,7 @@ export class CompactionManager {
|
|
|
100
139
|
const checkpoint = this.loop.getSessionCheckpoint();
|
|
101
140
|
const checkpointText = checkpoint.toContextString();
|
|
102
141
|
if (checkpointText) {
|
|
103
|
-
lines.push("", "--- Session Checkpoint (what you were doing
|
|
142
|
+
lines.push("", "--- Session Checkpoint (what you were doing + how it felt) ---", checkpointText);
|
|
104
143
|
}
|
|
105
144
|
// Include active agents
|
|
106
145
|
const agents = this.loop.getAgentRegistry().getAll().filter(a => a.status === "running");
|
|
@@ -110,6 +149,9 @@ export class CompactionManager {
|
|
|
110
149
|
lines.push(` - ${a.id}: ${a.description} (started ${new Date(a.startTime).toISOString()})`);
|
|
111
150
|
}
|
|
112
151
|
}
|
|
152
|
+
if (this.eventLoopActive) {
|
|
153
|
+
lines.push("", "CRITICAL: The event loop was running before compaction. Restart it NOW:", ' bash "$PWD/bin/event-loop"');
|
|
154
|
+
}
|
|
113
155
|
return lines.join("\n");
|
|
114
156
|
}
|
|
115
157
|
// R150: Consume-once compaction flag for digest vs full identity delivery
|
|
@@ -121,15 +163,54 @@ export class CompactionManager {
|
|
|
121
163
|
consumeCompactionFlag() {
|
|
122
164
|
if (this.compactedSinceLastWake) {
|
|
123
165
|
this.compactedSinceLastWake = false;
|
|
166
|
+
// Mark that the loop was restarted after this compaction
|
|
167
|
+
if (this.pendingCompaction) {
|
|
168
|
+
this.pendingCompaction.loopRestarted = true;
|
|
169
|
+
this.compactionHistory.push(this.pendingCompaction);
|
|
170
|
+
this.pendingCompaction = null;
|
|
171
|
+
this.logger.info(`Compaction #${this.compactionCount} — loop restarted successfully`);
|
|
172
|
+
}
|
|
124
173
|
return true;
|
|
125
174
|
}
|
|
126
175
|
return false;
|
|
127
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Called when /api/wait is invoked (even without compaction flag).
|
|
179
|
+
* If there's a pending compaction that hasn't been consumed yet,
|
|
180
|
+
* this means the loop restarted via normal wake, not post-compaction wake.
|
|
181
|
+
*/
|
|
182
|
+
markLoopActive() {
|
|
183
|
+
if (this.pendingCompaction && !this.compactedSinceLastWake) {
|
|
184
|
+
// Edge case: compaction happened but flag was already consumed
|
|
185
|
+
// This shouldn't normally happen, but handle it gracefully
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Called on first /api/wait — marks event loop as active for this backend lifetime */
|
|
189
|
+
setEventLoopActive() {
|
|
190
|
+
if (!this.eventLoopActive) {
|
|
191
|
+
this.eventLoopActive = true;
|
|
192
|
+
this.logger.info("Event loop marked active — will instruct restart after compaction");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
isEventLoopActive() {
|
|
196
|
+
return this.eventLoopActive;
|
|
197
|
+
}
|
|
128
198
|
getFlushState() {
|
|
129
199
|
return {
|
|
130
200
|
flushedThisCycle: this.flushedThisCycle,
|
|
131
201
|
lastFlushTimestamp: this.lastFlushTimestamp,
|
|
132
202
|
};
|
|
133
203
|
}
|
|
204
|
+
getCompactionStats() {
|
|
205
|
+
// A "failure" is a compaction where loopRestarted stayed false
|
|
206
|
+
// (pending compaction also counts as potentially failed if old enough)
|
|
207
|
+
const failures = this.compactionHistory.filter(c => !c.loopRestarted).length;
|
|
208
|
+
return {
|
|
209
|
+
count: this.compactionCount,
|
|
210
|
+
history: [...this.compactionHistory, ...(this.pendingCompaction ? [this.pendingCompaction] : [])],
|
|
211
|
+
pending: this.pendingCompaction,
|
|
212
|
+
loopFailures: failures,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
134
215
|
}
|
|
135
216
|
//# sourceMappingURL=compaction-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-manager.js","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,sDAAsD;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"compaction-manager.js","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../src/dashboard-server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAe/D,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB;IAkBvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,gBAAgB;IAclB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAe3B,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAalC,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../src/dashboard-server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAe/D,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB;IAkBvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,gBAAgB;IAclB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAe3B,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAalC,OAAO,CAAC,WAAW;IAyfnB,OAAO,CAAC,cAAc;IA4CtB,OAAO,CAAC,eAAe;IAuCvB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,SAAS;CAQlB"}
|
package/dist/dashboard-server.js
CHANGED
|
@@ -113,7 +113,12 @@ export class DashboardServer {
|
|
|
113
113
|
setupRoutes() {
|
|
114
114
|
// API endpoints
|
|
115
115
|
this.app.get("/api/status", (_req, res) => {
|
|
116
|
-
|
|
116
|
+
const status = this.loop.getStatus();
|
|
117
|
+
const compaction = this.compactionManager.getCompactionStats();
|
|
118
|
+
res.json({ ...status, compaction: { count: compaction.count, loopFailures: compaction.loopFailures, pending: !!compaction.pending } });
|
|
119
|
+
});
|
|
120
|
+
this.app.get("/api/compaction-stats", (_req, res) => {
|
|
121
|
+
res.json(this.compactionManager.getCompactionStats());
|
|
117
122
|
});
|
|
118
123
|
this.app.get("/api/events", (req, res) => {
|
|
119
124
|
const limit = parseInt(req.query.limit) || 50;
|
|
@@ -138,6 +143,8 @@ export class DashboardServer {
|
|
|
138
143
|
// Maintains a server-side delivery watermark to prevent replaying old events after compaction
|
|
139
144
|
// Optional `since` query param (ms timestamp) overrides the watermark
|
|
140
145
|
this.app.get("/api/wait", async (req, res) => {
|
|
146
|
+
// Mark event loop as active on first call — persists for backend lifetime
|
|
147
|
+
this.compactionManager.setEventLoopActive();
|
|
141
148
|
const timeout = Math.min(parseInt(req.query.timeout) || 30, 120) * 1000;
|
|
142
149
|
const sinceParam = req.query.since ? parseInt(req.query.since) : undefined;
|
|
143
150
|
try {
|
|
@@ -151,10 +158,12 @@ export class DashboardServer {
|
|
|
151
158
|
const identityPayload = needsFull
|
|
152
159
|
? { soul: identity.getSoul(), user: identity.getUser(), state: identity.getAgentState(), full: true }
|
|
153
160
|
: { digest: identity.getDigest(), full: false };
|
|
161
|
+
const compactionStats = this.compactionManager.getCompactionStats();
|
|
154
162
|
res.json({
|
|
155
163
|
identity: identityPayload,
|
|
156
164
|
events,
|
|
157
165
|
cursor: this.loop.getDeliveryWatermark(),
|
|
166
|
+
compaction: { count: compactionStats.count, loopFailures: compactionStats.loopFailures },
|
|
158
167
|
});
|
|
159
168
|
}
|
|
160
169
|
}
|
|
@@ -341,9 +350,19 @@ export class DashboardServer {
|
|
|
341
350
|
mkdirSync(dir, { recursive: true });
|
|
342
351
|
writeFileSync(kanbanDataPath, JSON.stringify(data, null, 2));
|
|
343
352
|
};
|
|
344
|
-
//
|
|
353
|
+
// Auto-flush done tasks older than 3 days
|
|
354
|
+
const flushDoneTasks = () => {
|
|
355
|
+
const data = readKanban();
|
|
356
|
+
const cutoff = Date.now() - 3 * 24 * 60 * 60 * 1000;
|
|
357
|
+
const before = data.tasks.length;
|
|
358
|
+
data.tasks = data.tasks.filter((t) => t.status !== "done" || new Date(t.updated).getTime() > cutoff);
|
|
359
|
+
if (data.tasks.length < before)
|
|
360
|
+
writeKanban(data);
|
|
361
|
+
return data;
|
|
362
|
+
};
|
|
363
|
+
// List all tasks (auto-flushes stale done tasks)
|
|
345
364
|
this.app.get("/api/kanban/tasks", (_req, res) => {
|
|
346
|
-
res.json(
|
|
365
|
+
res.json(flushDoneTasks().tasks);
|
|
347
366
|
});
|
|
348
367
|
// Create a task
|
|
349
368
|
this.app.post("/api/kanban/tasks", express.json(), (req, res) => {
|
|
@@ -390,7 +409,7 @@ export class DashboardServer {
|
|
|
390
409
|
res.json({ ok: true });
|
|
391
410
|
});
|
|
392
411
|
// --- CRM CRUD (markdown files with YAML frontmatter) ---
|
|
393
|
-
const crmDir =
|
|
412
|
+
const crmDir = resolve(import.meta.dirname ?? __dirname, "..", "local", "crm");
|
|
394
413
|
const parseCrmFile = (slug, content) => {
|
|
395
414
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
396
415
|
if (!fmMatch)
|
|
@@ -552,6 +571,41 @@ export class DashboardServer {
|
|
|
552
571
|
unlinkSync(filePath);
|
|
553
572
|
res.json({ ok: true });
|
|
554
573
|
});
|
|
574
|
+
// --- Document viewer endpoint ---
|
|
575
|
+
// Serves markdown files from allowed base directories
|
|
576
|
+
const projectDir = resolve(import.meta.dirname ?? __dirname, "..");
|
|
577
|
+
const halShareDir = resolve(projectDir, "../HalShare");
|
|
578
|
+
const homarusccDir = join(homedir(), ".homaruscc");
|
|
579
|
+
const allowedBases = {
|
|
580
|
+
"HalShare": halShareDir,
|
|
581
|
+
"~/.homaruscc": homarusccDir,
|
|
582
|
+
"crm": resolve(projectDir, "local", "crm"),
|
|
583
|
+
};
|
|
584
|
+
this.app.get("/api/docs", (req, res) => {
|
|
585
|
+
const filePath = req.query.path;
|
|
586
|
+
if (!filePath) {
|
|
587
|
+
res.status(400).json({ error: "path required" });
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
// Resolve against allowed bases
|
|
591
|
+
let resolved = null;
|
|
592
|
+
for (const [prefix, base] of Object.entries(allowedBases)) {
|
|
593
|
+
if (filePath.startsWith(prefix + "/") || filePath.startsWith(prefix + "\\")) {
|
|
594
|
+
const relative = filePath.slice(prefix.length + 1);
|
|
595
|
+
const full = resolve(base, relative);
|
|
596
|
+
// Prevent directory traversal
|
|
597
|
+
if (full.startsWith(base)) {
|
|
598
|
+
resolved = full;
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
if (!resolved || !existsSync(resolved)) {
|
|
604
|
+
res.status(404).json({ error: "Document not found" });
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
res.type("text/markdown").send(readFileSync(resolved, "utf8"));
|
|
608
|
+
});
|
|
555
609
|
// Serve built dashboard in production
|
|
556
610
|
const distPath = resolve(import.meta.dirname ?? __dirname, "../dashboard/dist");
|
|
557
611
|
if (existsSync(distPath)) {
|