homaruscc 0.2.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/.env.example +8 -0
- package/LICENSE +21 -0
- package/README.md +307 -0
- package/bin/event-loop +60 -0
- package/config.example.json +55 -0
- package/dist/agent-registry.d.ts +38 -0
- package/dist/agent-registry.d.ts.map +1 -0
- package/dist/agent-registry.js +207 -0
- package/dist/agent-registry.js.map +1 -0
- package/dist/backend.d.ts +3 -0
- package/dist/backend.d.ts.map +1 -0
- package/dist/backend.js +59 -0
- package/dist/backend.js.map +1 -0
- package/dist/browser-service.d.ts +19 -0
- package/dist/browser-service.d.ts.map +1 -0
- package/dist/browser-service.js +101 -0
- package/dist/browser-service.js.map +1 -0
- package/dist/channel-adapter.d.ts +22 -0
- package/dist/channel-adapter.d.ts.map +1 -0
- package/dist/channel-adapter.js +62 -0
- package/dist/channel-adapter.js.map +1 -0
- package/dist/channel-manager.d.ts +18 -0
- package/dist/channel-manager.d.ts.map +1 -0
- package/dist/channel-manager.js +73 -0
- package/dist/channel-manager.js.map +1 -0
- package/dist/compaction-manager.d.ts +23 -0
- package/dist/compaction-manager.d.ts.map +1 -0
- package/dist/compaction-manager.js +135 -0
- package/dist/compaction-manager.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +169 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard-adapter.d.ts +17 -0
- package/dist/dashboard-adapter.d.ts.map +1 -0
- package/dist/dashboard-adapter.js +41 -0
- package/dist/dashboard-adapter.js.map +1 -0
- package/dist/dashboard-server.d.ts +29 -0
- package/dist/dashboard-server.d.ts.map +1 -0
- package/dist/dashboard-server.js +381 -0
- package/dist/dashboard-server.js.map +1 -0
- package/dist/embedding-provider.d.ts +28 -0
- package/dist/embedding-provider.d.ts.map +1 -0
- package/dist/embedding-provider.js +91 -0
- package/dist/embedding-provider.js.map +1 -0
- package/dist/event-bus.d.ts +18 -0
- package/dist/event-bus.d.ts.map +1 -0
- package/dist/event-bus.js +41 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/event-queue.d.ts +18 -0
- package/dist/event-queue.d.ts.map +1 -0
- package/dist/event-queue.js +68 -0
- package/dist/event-queue.js.map +1 -0
- package/dist/homaruscc.d.ts +69 -0
- package/dist/homaruscc.d.ts.map +1 -0
- package/dist/homaruscc.js +337 -0
- package/dist/homaruscc.js.map +1 -0
- package/dist/identity-manager.d.ts +36 -0
- package/dist/identity-manager.d.ts.map +1 -0
- package/dist/identity-manager.js +142 -0
- package/dist/identity-manager.js.map +1 -0
- package/dist/mcp-proxy.d.ts +3 -0
- package/dist/mcp-proxy.d.ts.map +1 -0
- package/dist/mcp-proxy.js +259 -0
- package/dist/mcp-proxy.js.map +1 -0
- package/dist/mcp-resources.d.ts +10 -0
- package/dist/mcp-resources.d.ts.map +1 -0
- package/dist/mcp-resources.js +67 -0
- package/dist/mcp-resources.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +169 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-tools.d.ts +14 -0
- package/dist/mcp-tools.d.ts.map +1 -0
- package/dist/mcp-tools.js +408 -0
- package/dist/mcp-tools.js.map +1 -0
- package/dist/memory-index.d.ts +79 -0
- package/dist/memory-index.d.ts.map +1 -0
- package/dist/memory-index.js +437 -0
- package/dist/memory-index.js.map +1 -0
- package/dist/session-checkpoint.d.ts +22 -0
- package/dist/session-checkpoint.d.ts.map +1 -0
- package/dist/session-checkpoint.js +100 -0
- package/dist/session-checkpoint.js.map +1 -0
- package/dist/skill-manager.d.ts +26 -0
- package/dist/skill-manager.d.ts.map +1 -0
- package/dist/skill-manager.js +156 -0
- package/dist/skill-manager.js.map +1 -0
- package/dist/skill-transport.d.ts +45 -0
- package/dist/skill-transport.d.ts.map +1 -0
- package/dist/skill-transport.js +111 -0
- package/dist/skill-transport.js.map +1 -0
- package/dist/skill.d.ts +22 -0
- package/dist/skill.d.ts.map +1 -0
- package/dist/skill.js +106 -0
- package/dist/skill.js.map +1 -0
- package/dist/telegram-adapter.d.ts +43 -0
- package/dist/telegram-adapter.d.ts.map +1 -0
- package/dist/telegram-adapter.js +188 -0
- package/dist/telegram-adapter.js.map +1 -0
- package/dist/timer-service.d.ts +30 -0
- package/dist/timer-service.d.ts.map +1 -0
- package/dist/timer-service.js +176 -0
- package/dist/timer-service.js.map +1 -0
- package/dist/tool-registry.d.ts +30 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +108 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/tools/bash.d.ts +3 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +67 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/browser.d.ts +4 -0
- package/dist/tools/browser.d.ts.map +1 -0
- package/dist/tools/browser.js +138 -0
- package/dist/tools/browser.js.map +1 -0
- package/dist/tools/edit.d.ts +3 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +47 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/git.d.ts +3 -0
- package/dist/tools/git.d.ts.map +1 -0
- package/dist/tools/git.js +105 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/glob.d.ts +3 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +84 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +3 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +168 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +46 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/memory.d.ts +4 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +64 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/read.d.ts +3 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +50 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.d.ts.map +1 -0
- package/dist/tools/web-fetch.js +51 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +3 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +65 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write.d.ts +3 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +32 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/transcript-logger.d.ts +23 -0
- package/dist/transcript-logger.d.ts.map +1 -0
- package/dist/transcript-logger.js +101 -0
- package/dist/transcript-logger.js.map +1 -0
- package/dist/types.d.ts +190 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/identity.example/disagreements.md +9 -0
- package/identity.example/preferences.md +11 -0
- package/identity.example/soul.md +12 -0
- package/identity.example/state.md +21 -0
- package/identity.example/user.md +14 -0
- package/package.json +60 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// CRC: crc-AgentRegistry.md | Seq: seq-agent-dispatch.md, seq-agent-poll.md
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { statSync, openSync, readSync, closeSync } from "node:fs";
|
|
4
|
+
// R156: Completion markers found in Task agent JSONL output
|
|
5
|
+
const COMPLETION_MARKERS = ['"stop_reason":"end_turn"', '"type":"result"'];
|
|
6
|
+
// R156: Stable mtime threshold (ms) — file unchanged for this long is considered complete
|
|
7
|
+
const STABLE_THRESHOLD_MS = 10_000;
|
|
8
|
+
// R156: Number of bytes to read from file tail for marker detection
|
|
9
|
+
const TAIL_BYTES = 512;
|
|
10
|
+
export class AgentRegistry {
|
|
11
|
+
agents = new Map();
|
|
12
|
+
maxConcurrent;
|
|
13
|
+
emitFn = null;
|
|
14
|
+
logger;
|
|
15
|
+
// R155: Configurable poll interval
|
|
16
|
+
pollIntervalMs;
|
|
17
|
+
// R159: Global poll timer handle
|
|
18
|
+
pollTimer = null;
|
|
19
|
+
constructor(logger, maxConcurrent = 3, pollIntervalMs = 5000) {
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
this.maxConcurrent = maxConcurrent;
|
|
22
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
23
|
+
}
|
|
24
|
+
setEmitter(fn) {
|
|
25
|
+
this.emitFn = fn;
|
|
26
|
+
}
|
|
27
|
+
register(id, description, outputFile) {
|
|
28
|
+
const active = this.getActiveCount();
|
|
29
|
+
if (active >= this.maxConcurrent) {
|
|
30
|
+
this.logger.warn("Agent registry at capacity", { active, max: this.maxConcurrent });
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
this.agents.set(id, {
|
|
34
|
+
id,
|
|
35
|
+
description,
|
|
36
|
+
status: "running",
|
|
37
|
+
startTime: Date.now(),
|
|
38
|
+
outputFile,
|
|
39
|
+
});
|
|
40
|
+
this.logger.info("Agent registered", { id, description });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
getAll() {
|
|
44
|
+
return Array.from(this.agents.values());
|
|
45
|
+
}
|
|
46
|
+
get(id) {
|
|
47
|
+
return this.agents.get(id) ?? null;
|
|
48
|
+
}
|
|
49
|
+
// R157, R162: Complete only if still running (prevents duplicate events)
|
|
50
|
+
complete(id, result) {
|
|
51
|
+
const agent = this.resolve(id);
|
|
52
|
+
if (!agent)
|
|
53
|
+
return;
|
|
54
|
+
if (agent.status !== "running")
|
|
55
|
+
return;
|
|
56
|
+
agent.status = "completed";
|
|
57
|
+
agent.result = result;
|
|
58
|
+
this.emit("agent_completed", id, agent.description, { result });
|
|
59
|
+
this.logger.info("Agent completed", { id, description: agent.description });
|
|
60
|
+
}
|
|
61
|
+
fail(id, error) {
|
|
62
|
+
const agent = this.resolve(id);
|
|
63
|
+
if (!agent)
|
|
64
|
+
return;
|
|
65
|
+
agent.status = "failed";
|
|
66
|
+
agent.error = error;
|
|
67
|
+
this.emit("agent_failed", id, agent.description, { error });
|
|
68
|
+
this.logger.warn("Agent failed", { id, error });
|
|
69
|
+
}
|
|
70
|
+
// R160: Cleanup removes agent and any associated polling state
|
|
71
|
+
cleanup(id) {
|
|
72
|
+
this.agents.delete(id);
|
|
73
|
+
}
|
|
74
|
+
getAvailableSlots() {
|
|
75
|
+
return Math.max(0, this.maxConcurrent - this.getActiveCount());
|
|
76
|
+
}
|
|
77
|
+
getActiveCount() {
|
|
78
|
+
let count = 0;
|
|
79
|
+
for (const agent of this.agents.values()) {
|
|
80
|
+
if (agent.status === "running")
|
|
81
|
+
count++;
|
|
82
|
+
}
|
|
83
|
+
return count;
|
|
84
|
+
}
|
|
85
|
+
// R159: Start global polling interval
|
|
86
|
+
startPolling() {
|
|
87
|
+
if (this.pollTimer)
|
|
88
|
+
return;
|
|
89
|
+
this.pollTimer = setInterval(() => this.pollAgents(), this.pollIntervalMs);
|
|
90
|
+
// Unref so the timer does not keep the process alive during shutdown
|
|
91
|
+
if (this.pollTimer && typeof this.pollTimer === "object" && "unref" in this.pollTimer) {
|
|
92
|
+
this.pollTimer.unref();
|
|
93
|
+
}
|
|
94
|
+
this.logger.info("Agent completion polling started", { intervalMs: this.pollIntervalMs });
|
|
95
|
+
}
|
|
96
|
+
// R159: Stop global polling interval
|
|
97
|
+
stopPolling() {
|
|
98
|
+
if (this.pollTimer) {
|
|
99
|
+
clearInterval(this.pollTimer);
|
|
100
|
+
this.pollTimer = null;
|
|
101
|
+
this.logger.info("Agent completion polling stopped");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// R154, R158: Poll all running agents with outputFiles for completion
|
|
105
|
+
pollAgents() {
|
|
106
|
+
for (const agent of this.agents.values()) {
|
|
107
|
+
// R158: Only poll running agents with an outputFile
|
|
108
|
+
if (agent.status !== "running" || !agent.outputFile)
|
|
109
|
+
continue;
|
|
110
|
+
try {
|
|
111
|
+
this.checkAgentFile(agent);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
// R161: Log and skip on errors
|
|
115
|
+
this.logger.debug("Poll check error for agent", {
|
|
116
|
+
id: agent.id,
|
|
117
|
+
error: String(err),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
checkAgentFile(agent) {
|
|
123
|
+
// R161: statSync may throw ENOENT if file does not exist yet
|
|
124
|
+
let stat;
|
|
125
|
+
try {
|
|
126
|
+
stat = statSync(agent.outputFile);
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return; // File does not exist yet — skip
|
|
130
|
+
}
|
|
131
|
+
// Skip empty files
|
|
132
|
+
if (stat.size === 0)
|
|
133
|
+
return;
|
|
134
|
+
// R156: Read the tail of the file
|
|
135
|
+
const tail = this.readTail(agent.outputFile, stat.size);
|
|
136
|
+
if (!tail)
|
|
137
|
+
return;
|
|
138
|
+
// R156: Check for completion markers in the tail
|
|
139
|
+
const hasMarker = COMPLETION_MARKERS.some((m) => tail.includes(m));
|
|
140
|
+
if (hasMarker) {
|
|
141
|
+
this.logger.info("Detected completion marker in agent output", { id: agent.id });
|
|
142
|
+
this.complete(agent.id, this.extractSummary(tail));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// R156: Check for stable mtime (no writes in STABLE_THRESHOLD_MS)
|
|
146
|
+
const age = Date.now() - stat.mtimeMs;
|
|
147
|
+
if (age >= STABLE_THRESHOLD_MS) {
|
|
148
|
+
this.logger.info("Detected stable output file for agent", {
|
|
149
|
+
id: agent.id,
|
|
150
|
+
stableForMs: Math.round(age),
|
|
151
|
+
});
|
|
152
|
+
this.complete(agent.id, this.extractSummary(tail));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Read the last `count` bytes of a file as a UTF-8 string
|
|
156
|
+
readTail(filePath, fileSize) {
|
|
157
|
+
const readSize = Math.min(TAIL_BYTES, fileSize);
|
|
158
|
+
const offset = fileSize - readSize;
|
|
159
|
+
const buf = Buffer.alloc(readSize);
|
|
160
|
+
let fd = null;
|
|
161
|
+
try {
|
|
162
|
+
fd = openSync(filePath, "r");
|
|
163
|
+
readSync(fd, buf, 0, readSize, offset);
|
|
164
|
+
return buf.toString("utf-8");
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
if (fd !== null) {
|
|
171
|
+
try {
|
|
172
|
+
closeSync(fd);
|
|
173
|
+
}
|
|
174
|
+
catch { /* ignore */ }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Extract a brief summary from the file tail for the completion result
|
|
179
|
+
extractSummary(tail) {
|
|
180
|
+
// Try to find the last JSON line that looks like a result
|
|
181
|
+
const lines = tail.split("\n").filter((l) => l.trim().length > 0);
|
|
182
|
+
const lastLine = lines[lines.length - 1] ?? "";
|
|
183
|
+
// Truncate to a reasonable length for the event payload
|
|
184
|
+
if (lastLine.length > 200) {
|
|
185
|
+
return lastLine.slice(0, 200) + "...";
|
|
186
|
+
}
|
|
187
|
+
return lastLine || "(output file completed)";
|
|
188
|
+
}
|
|
189
|
+
resolve(id) {
|
|
190
|
+
const agent = this.agents.get(id);
|
|
191
|
+
if (!agent) {
|
|
192
|
+
this.logger.warn("Unknown agent", { id });
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return agent;
|
|
196
|
+
}
|
|
197
|
+
emit(type, agentId, description, extra) {
|
|
198
|
+
this.emitFn?.({
|
|
199
|
+
id: randomUUID(),
|
|
200
|
+
type,
|
|
201
|
+
source: `agent:${agentId}`,
|
|
202
|
+
timestamp: Date.now(),
|
|
203
|
+
payload: { agentId, description, ...extra },
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=agent-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-registry.js","sourceRoot":"","sources":["../src/agent-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":""}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// CRC: crc-Backend.md | Seq: seq-startup.md
|
|
3
|
+
// HomarUScc backend — standalone process (no MCP stdio)
|
|
4
|
+
// Used by mcp-proxy.ts or as a standalone server.
|
|
5
|
+
import { HomarUScc } from "./homaruscc.js";
|
|
6
|
+
import { DashboardServer } from "./dashboard-server.js";
|
|
7
|
+
import { DashboardAdapter } from "./dashboard-adapter.js";
|
|
8
|
+
const logger = {
|
|
9
|
+
debug(msg, meta) {
|
|
10
|
+
if (process.env.HOMARUSCC_DEBUG) {
|
|
11
|
+
process.stderr.write(`[DEBUG] ${msg} ${meta ? JSON.stringify(meta) : ""}\n`);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
info(msg, meta) {
|
|
15
|
+
process.stderr.write(`[INFO] ${msg} ${meta ? JSON.stringify(meta) : ""}\n`);
|
|
16
|
+
},
|
|
17
|
+
warn(msg, meta) {
|
|
18
|
+
process.stderr.write(`[WARN] ${msg} ${meta ? JSON.stringify(meta) : ""}\n`);
|
|
19
|
+
},
|
|
20
|
+
error(msg, meta) {
|
|
21
|
+
process.stderr.write(`[ERROR] ${msg} ${meta ? JSON.stringify(meta) : ""}\n`);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
async function main() {
|
|
25
|
+
const configPath = process.env.HOMARUSCC_CONFIG ?? undefined;
|
|
26
|
+
const loop = new HomarUScc(logger, configPath);
|
|
27
|
+
await loop.start();
|
|
28
|
+
// Dashboard adapter (channel)
|
|
29
|
+
const dashboardConfig = loop.getConfig().getAll().dashboard;
|
|
30
|
+
let dashboardServer = null;
|
|
31
|
+
if (dashboardConfig?.enabled !== false) {
|
|
32
|
+
const dashboardAdapter = new DashboardAdapter(logger);
|
|
33
|
+
loop.getChannelManager().registerAdapter(dashboardAdapter);
|
|
34
|
+
await dashboardAdapter.connect();
|
|
35
|
+
dashboardAdapter.onMessage((event) => loop.emit(event));
|
|
36
|
+
dashboardServer = new DashboardServer(logger, dashboardConfig?.port ?? 3120, loop, dashboardAdapter);
|
|
37
|
+
await dashboardServer.start();
|
|
38
|
+
// Forward event loop notifications to dashboard WebSocket clients
|
|
39
|
+
loop.setNotifyFn((event) => {
|
|
40
|
+
dashboardServer?.broadcastEvent(event);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
logger.info("HomarUScc backend running (no MCP stdio)");
|
|
44
|
+
// Graceful shutdown
|
|
45
|
+
const shutdown = async () => {
|
|
46
|
+
logger.info("Backend shutting down...");
|
|
47
|
+
if (dashboardServer)
|
|
48
|
+
await dashboardServer.stop();
|
|
49
|
+
await loop.stop();
|
|
50
|
+
process.exit(0);
|
|
51
|
+
};
|
|
52
|
+
process.on("SIGINT", shutdown);
|
|
53
|
+
process.on("SIGTERM", shutdown);
|
|
54
|
+
}
|
|
55
|
+
main().catch((err) => {
|
|
56
|
+
process.stderr.write(`[FATAL] ${String(err)}\n`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BrowserConfig, Logger } from "./types.js";
|
|
2
|
+
export declare class BrowserService {
|
|
3
|
+
private browser;
|
|
4
|
+
private persistentContext;
|
|
5
|
+
private page;
|
|
6
|
+
private config;
|
|
7
|
+
private logger;
|
|
8
|
+
constructor(logger: Logger, config: BrowserConfig);
|
|
9
|
+
private ensureBrowser;
|
|
10
|
+
navigate(url: string): Promise<string>;
|
|
11
|
+
snapshot(): Promise<string>;
|
|
12
|
+
screenshot(): Promise<string>;
|
|
13
|
+
click(selector: string): Promise<string>;
|
|
14
|
+
type(selector: string, text: string): Promise<string>;
|
|
15
|
+
evaluate(script: string): Promise<string>;
|
|
16
|
+
getContent(): Promise<string>;
|
|
17
|
+
stop(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=browser-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-service.d.ts","sourceRoot":"","sources":["../src/browser-service.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAExD,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa;YAKnC,aAAa;IAkDrB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOtC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAM3B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAM7B,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMxC,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMrD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMzC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAK7B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAW5B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
export class BrowserService {
|
|
2
|
+
browser = null;
|
|
3
|
+
persistentContext = null;
|
|
4
|
+
page = null;
|
|
5
|
+
config;
|
|
6
|
+
logger;
|
|
7
|
+
constructor(logger, config) {
|
|
8
|
+
this.logger = logger;
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async ensureBrowser() {
|
|
12
|
+
if (this.page)
|
|
13
|
+
return this.page;
|
|
14
|
+
const { chromium } = await import("playwright");
|
|
15
|
+
const headless = this.config.headless ?? true;
|
|
16
|
+
const viewport = this.config.viewport ?? { width: 1280, height: 720 };
|
|
17
|
+
if (this.config.userDataDir) {
|
|
18
|
+
// Persistent context — reuses cookies, localStorage, sessions across launches
|
|
19
|
+
this.logger.info("Launching persistent browser", {
|
|
20
|
+
headless,
|
|
21
|
+
userDataDir: this.config.userDataDir,
|
|
22
|
+
});
|
|
23
|
+
const context = await chromium.launchPersistentContext(this.config.userDataDir, {
|
|
24
|
+
headless,
|
|
25
|
+
viewport,
|
|
26
|
+
...(this.config.executablePath && {
|
|
27
|
+
executablePath: this.config.executablePath,
|
|
28
|
+
}),
|
|
29
|
+
...(this.config.proxy && { proxy: { server: this.config.proxy } }),
|
|
30
|
+
});
|
|
31
|
+
// launchPersistentContext returns a BrowserContext, not a Browser
|
|
32
|
+
this.persistentContext = context;
|
|
33
|
+
this.page = context.pages()[0] ?? (await context.newPage());
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Ephemeral context — clean session each time
|
|
37
|
+
const launchOptions = { headless };
|
|
38
|
+
if (this.config.executablePath) {
|
|
39
|
+
launchOptions.executablePath = this.config.executablePath;
|
|
40
|
+
}
|
|
41
|
+
if (this.config.proxy) {
|
|
42
|
+
launchOptions.proxy = { server: this.config.proxy };
|
|
43
|
+
}
|
|
44
|
+
this.logger.info("Launching browser", { headless });
|
|
45
|
+
this.browser = await chromium.launch(launchOptions);
|
|
46
|
+
const context = await this.browser.newContext({ viewport });
|
|
47
|
+
this.page = await context.newPage();
|
|
48
|
+
}
|
|
49
|
+
this.page.setDefaultTimeout(this.config.timeout ?? 30000);
|
|
50
|
+
this.logger.info("Browser ready");
|
|
51
|
+
return this.page;
|
|
52
|
+
}
|
|
53
|
+
async navigate(url) {
|
|
54
|
+
const page = await this.ensureBrowser();
|
|
55
|
+
await page.goto(url, { waitUntil: "domcontentloaded" });
|
|
56
|
+
const title = await page.title();
|
|
57
|
+
return `Navigated to: ${page.url()}\nTitle: ${title}`;
|
|
58
|
+
}
|
|
59
|
+
async snapshot() {
|
|
60
|
+
const page = await this.ensureBrowser();
|
|
61
|
+
const snap = await page.locator("body").ariaSnapshot();
|
|
62
|
+
return snap || "Empty accessibility snapshot";
|
|
63
|
+
}
|
|
64
|
+
async screenshot() {
|
|
65
|
+
const page = await this.ensureBrowser();
|
|
66
|
+
const buffer = await page.screenshot({ type: "png" });
|
|
67
|
+
return buffer.toString("base64");
|
|
68
|
+
}
|
|
69
|
+
async click(selector) {
|
|
70
|
+
const page = await this.ensureBrowser();
|
|
71
|
+
await page.click(selector);
|
|
72
|
+
return `Clicked: ${selector}`;
|
|
73
|
+
}
|
|
74
|
+
async type(selector, text) {
|
|
75
|
+
const page = await this.ensureBrowser();
|
|
76
|
+
await page.fill(selector, text);
|
|
77
|
+
return `Typed into: ${selector}`;
|
|
78
|
+
}
|
|
79
|
+
async evaluate(script) {
|
|
80
|
+
const page = await this.ensureBrowser();
|
|
81
|
+
const result = await page.evaluate(script);
|
|
82
|
+
return typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
83
|
+
}
|
|
84
|
+
async getContent() {
|
|
85
|
+
const page = await this.ensureBrowser();
|
|
86
|
+
return await page.evaluate(() => document.body.innerText);
|
|
87
|
+
}
|
|
88
|
+
async stop() {
|
|
89
|
+
this.logger.info("Closing browser");
|
|
90
|
+
if (this.persistentContext) {
|
|
91
|
+
await this.persistentContext.close();
|
|
92
|
+
this.persistentContext = null;
|
|
93
|
+
}
|
|
94
|
+
else if (this.browser) {
|
|
95
|
+
await this.browser.close();
|
|
96
|
+
this.browser = null;
|
|
97
|
+
}
|
|
98
|
+
this.page = null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=browser-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-service.js","sourceRoot":"","sources":["../src/browser-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Event, MessagePayload, OutboundMessage, DmPolicy, GroupPolicy, HealthStatus, Logger } from "./types.js";
|
|
2
|
+
export type AdapterState = "disconnected" | "connecting" | "connected" | "error";
|
|
3
|
+
export declare abstract class ChannelAdapter {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
protected state: AdapterState;
|
|
6
|
+
protected dmPolicy: DmPolicy;
|
|
7
|
+
protected groupPolicy: GroupPolicy;
|
|
8
|
+
protected logger: Logger;
|
|
9
|
+
private messageHandler;
|
|
10
|
+
constructor(name: string, logger: Logger, dmPolicy?: DmPolicy, groupPolicy?: GroupPolicy);
|
|
11
|
+
getState(): AdapterState;
|
|
12
|
+
abstract connect(): Promise<void>;
|
|
13
|
+
abstract disconnect(): Promise<void>;
|
|
14
|
+
abstract send(target: string, message: OutboundMessage): Promise<void>;
|
|
15
|
+
abstract health(): HealthStatus;
|
|
16
|
+
onMessage(handler: (event: Event) => void): void;
|
|
17
|
+
protected normalizeInbound(payload: MessagePayload, target?: string): Event;
|
|
18
|
+
protected checkAccess(payload: MessagePayload): boolean;
|
|
19
|
+
protected deliver(payload: MessagePayload): void;
|
|
20
|
+
protected deliverWithTarget(payload: MessagePayload, target: string): void;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=channel-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-adapter.d.ts","sourceRoot":"","sources":["../src/channel-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EACpF,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;AAEjF,8BAAsB,cAAc;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,KAAK,EAAE,YAAY,CAAkB;IAC/C,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC7B,SAAS,CAAC,WAAW,EAAE,WAAW,CAAC;IACnC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,cAAc,CAAyC;gBAEnD,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAiB,EAAE,WAAW,GAAE,WAAgC;IAOpH,QAAQ,IAAI,YAAY;IAIxB,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IACpC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IACtE,QAAQ,CAAC,MAAM,IAAI,YAAY;IAE/B,SAAS,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAIhD,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK;IAW3E,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO;IAUvD,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAShD,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAQ3E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// CRC: crc-ChannelAdapter.md | Seq: seq-event-flow.md
|
|
2
|
+
// Channel adapter base — from HomarUS (removed CLIChannelAdapter, not needed for MCP)
|
|
3
|
+
import { v4 as uuid } from "uuid";
|
|
4
|
+
export class ChannelAdapter {
|
|
5
|
+
name;
|
|
6
|
+
state = "disconnected";
|
|
7
|
+
dmPolicy;
|
|
8
|
+
groupPolicy;
|
|
9
|
+
logger;
|
|
10
|
+
messageHandler = null;
|
|
11
|
+
constructor(name, logger, dmPolicy = "open", groupPolicy = "mention_required") {
|
|
12
|
+
this.name = name;
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
this.dmPolicy = dmPolicy;
|
|
15
|
+
this.groupPolicy = groupPolicy;
|
|
16
|
+
}
|
|
17
|
+
getState() {
|
|
18
|
+
return this.state;
|
|
19
|
+
}
|
|
20
|
+
onMessage(handler) {
|
|
21
|
+
this.messageHandler = handler;
|
|
22
|
+
}
|
|
23
|
+
normalizeInbound(payload, target) {
|
|
24
|
+
const source = target ? `channel:${this.name}:${target}` : `channel:${this.name}`;
|
|
25
|
+
return {
|
|
26
|
+
id: uuid(),
|
|
27
|
+
type: "message",
|
|
28
|
+
source,
|
|
29
|
+
timestamp: Date.now(),
|
|
30
|
+
payload,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
checkAccess(payload) {
|
|
34
|
+
if (payload.isGroup) {
|
|
35
|
+
if (this.groupPolicy === "disabled")
|
|
36
|
+
return false;
|
|
37
|
+
if (this.groupPolicy === "mention_required" && !payload.isMention)
|
|
38
|
+
return false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (this.dmPolicy === "disabled")
|
|
42
|
+
return false;
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
deliver(payload) {
|
|
46
|
+
if (!this.checkAccess(payload)) {
|
|
47
|
+
this.logger.debug("Message rejected by policy", { channel: this.name, from: payload.from });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const event = this.normalizeInbound(payload);
|
|
51
|
+
this.messageHandler?.(event);
|
|
52
|
+
}
|
|
53
|
+
deliverWithTarget(payload, target) {
|
|
54
|
+
if (!this.checkAccess(payload)) {
|
|
55
|
+
this.logger.debug("Message rejected by policy", { channel: this.name, from: payload.from });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const event = this.normalizeInbound(payload, target);
|
|
59
|
+
this.messageHandler?.(event);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=channel-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-adapter.js","sourceRoot":"","sources":["../src/channel-adapter.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,sFAAsF;AACtF,OAAO,EAAE,EAAE,IAAI,IAAI,EAAE,MAAM,MAAM,CAAC;AAOlC,MAAM,OAAgB,cAAc;IACzB,IAAI,CAAS;IACZ,KAAK,GAAiB,cAAc,CAAC;IACrC,QAAQ,CAAW;IACnB,WAAW,CAAc;IACzB,MAAM,CAAS;IACjB,cAAc,GAAoC,IAAI,CAAC;IAE/D,YAAY,IAAY,EAAE,MAAc,EAAE,WAAqB,MAAM,EAAE,cAA2B,kBAAkB;QAClH,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAOD,SAAS,CAAC,OAA+B;QACvC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;IAChC,CAAC;IAES,gBAAgB,CAAC,OAAuB,EAAE,MAAe;QACjE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC;QAClF,OAAO;YACL,EAAE,EAAE,IAAI,EAAE;YACV,IAAI,EAAE,SAAS;YACf,MAAM;YACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,OAAO;SACR,CAAC;IACJ,CAAC;IAES,WAAW,CAAC,OAAuB;QAC3C,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,IAAI,CAAC,WAAW,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC;YAClD,IAAI,IAAI,CAAC,WAAW,KAAK,kBAAkB,IAAI,CAAC,OAAO,CAAC,SAAS;gBAAE,OAAO,KAAK,CAAC;YAChF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAES,OAAO,CAAC,OAAuB;QACvC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5F,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAES,iBAAiB,CAAC,OAAuB,EAAE,MAAc;QACjE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5F,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACrD,IAAI,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Event, OutboundMessage, ChannelConfig, HealthStatus, Logger } from "./types.js";
|
|
2
|
+
import { ChannelAdapter } from "./channel-adapter.js";
|
|
3
|
+
export declare class ChannelManager {
|
|
4
|
+
private adapters;
|
|
5
|
+
private logger;
|
|
6
|
+
private eventHandler;
|
|
7
|
+
constructor(logger: Logger);
|
|
8
|
+
setEventHandler(fn: (event: Event) => void): void;
|
|
9
|
+
loadAdapters(channels: Record<string, ChannelConfig>): void;
|
|
10
|
+
registerAdapter(adapter: ChannelAdapter): void;
|
|
11
|
+
connectAll(): Promise<void>;
|
|
12
|
+
disconnectAll(): Promise<void>;
|
|
13
|
+
send(channel: string, target: string, message: OutboundMessage): Promise<void>;
|
|
14
|
+
getAdapter(name: string): ChannelAdapter | undefined;
|
|
15
|
+
getConnected(): ChannelAdapter[];
|
|
16
|
+
healthCheck(): Record<string, HealthStatus>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=channel-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-manager.d.ts","sourceRoot":"","sources":["../src/channel-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAyC;gBAEjD,MAAM,EAAE,MAAM;IAI1B,eAAe,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAIjD,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,IAAI;IAkB3D,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIxC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpF,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIpD,YAAY,IAAI,cAAc,EAAE;IAIhC,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;CAO5C"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { TelegramChannelAdapter } from "./telegram-adapter.js";
|
|
2
|
+
export class ChannelManager {
|
|
3
|
+
adapters = new Map();
|
|
4
|
+
logger;
|
|
5
|
+
eventHandler = null;
|
|
6
|
+
constructor(logger) {
|
|
7
|
+
this.logger = logger;
|
|
8
|
+
}
|
|
9
|
+
setEventHandler(fn) {
|
|
10
|
+
this.eventHandler = fn;
|
|
11
|
+
}
|
|
12
|
+
loadAdapters(channels) {
|
|
13
|
+
for (const [name, config] of Object.entries(channels)) {
|
|
14
|
+
let adapter;
|
|
15
|
+
switch (name) {
|
|
16
|
+
case "telegram":
|
|
17
|
+
adapter = TelegramChannelAdapter.fromChannelConfig(config, this.logger);
|
|
18
|
+
break;
|
|
19
|
+
default:
|
|
20
|
+
this.logger.warn("Unknown channel type, skipping", { name });
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
this.adapters.set(name, adapter);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Register an externally-created adapter (e.g., dashboard adapter)
|
|
27
|
+
registerAdapter(adapter) {
|
|
28
|
+
this.adapters.set(adapter.name, adapter);
|
|
29
|
+
}
|
|
30
|
+
async connectAll() {
|
|
31
|
+
for (const [name, adapter] of this.adapters) {
|
|
32
|
+
try {
|
|
33
|
+
adapter.onMessage((event) => this.eventHandler?.(event));
|
|
34
|
+
await adapter.connect();
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
this.logger.error("Failed to connect channel", { name, error: String(err) });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
this.logger.info("Channels connected", { count: this.adapters.size });
|
|
41
|
+
}
|
|
42
|
+
async disconnectAll() {
|
|
43
|
+
for (const [name, adapter] of this.adapters) {
|
|
44
|
+
try {
|
|
45
|
+
await adapter.disconnect();
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
this.logger.warn("Error disconnecting channel", { name, error: String(err) });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async send(channel, target, message) {
|
|
53
|
+
const adapter = this.adapters.get(channel);
|
|
54
|
+
if (!adapter) {
|
|
55
|
+
throw new Error(`Unknown channel: ${channel}`);
|
|
56
|
+
}
|
|
57
|
+
await adapter.send(target, message);
|
|
58
|
+
}
|
|
59
|
+
getAdapter(name) {
|
|
60
|
+
return this.adapters.get(name);
|
|
61
|
+
}
|
|
62
|
+
getConnected() {
|
|
63
|
+
return [...this.adapters.values()].filter((a) => a.getState() === "connected");
|
|
64
|
+
}
|
|
65
|
+
healthCheck() {
|
|
66
|
+
const results = {};
|
|
67
|
+
for (const [name, adapter] of this.adapters) {
|
|
68
|
+
results[name] = adapter.health();
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=channel-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-manager.js","sourceRoot":"","sources":["../src/channel-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Logger } from "./types.js";
|
|
2
|
+
import type { HomarUScc } from "./homaruscc.js";
|
|
3
|
+
export declare class CompactionManager {
|
|
4
|
+
private flushedThisCycle;
|
|
5
|
+
private lastFlushTimestamp;
|
|
6
|
+
private compactedSinceLastWake;
|
|
7
|
+
private logger;
|
|
8
|
+
private loop;
|
|
9
|
+
constructor(loop: HomarUScc, logger: Logger);
|
|
10
|
+
handlePreCompact(): string;
|
|
11
|
+
handlePostCompact(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Returns true if compaction occurred since the last /api/wait delivery.
|
|
14
|
+
* Consuming this resets the flag — the next call returns false until
|
|
15
|
+
* another compaction happens.
|
|
16
|
+
*/
|
|
17
|
+
consumeCompactionFlag(): boolean;
|
|
18
|
+
getFlushState(): {
|
|
19
|
+
flushedThisCycle: boolean;
|
|
20
|
+
lastFlushTimestamp: number;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=compaction-manager.d.ts.map
|