mono-pilot 0.2.10 → 0.2.13
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 +260 -2
- package/dist/src/agents-paths.js +36 -0
- package/dist/src/brief/blocks.js +83 -0
- package/dist/src/brief/defaults.js +60 -0
- package/dist/src/brief/frontmatter.js +53 -0
- package/dist/src/brief/paths.js +10 -0
- package/dist/src/brief/reflection.js +27 -0
- package/dist/src/cli.js +62 -5
- package/dist/src/cluster/bus.js +102 -0
- package/dist/src/cluster/follower.js +137 -0
- package/dist/src/cluster/init.js +182 -0
- package/dist/src/cluster/leader.js +97 -0
- package/dist/src/cluster/log.js +49 -0
- package/dist/src/cluster/protocol.js +34 -0
- package/dist/src/cluster/services/bus.js +243 -0
- package/dist/src/cluster/services/embedding.js +12 -0
- package/dist/src/cluster/socket.js +86 -0
- package/dist/src/cluster/test-bus.js +175 -0
- package/dist/src/cluster_v2/connection-lifecycle.js +31 -0
- package/dist/src/cluster_v2/connection-lifecycle.test.js +24 -0
- package/dist/src/cluster_v2/connection.js +159 -0
- package/dist/src/cluster_v2/connection.test.js +55 -0
- package/dist/src/cluster_v2/events.js +102 -0
- package/dist/src/cluster_v2/index.js +2 -0
- package/dist/src/cluster_v2/observability.js +99 -0
- package/dist/src/cluster_v2/observability.test.js +46 -0
- package/dist/src/cluster_v2/rpc.js +389 -0
- package/dist/src/cluster_v2/rpc.test.js +110 -0
- package/dist/src/cluster_v2/runtime.failover.integration.test.js +156 -0
- package/dist/src/cluster_v2/runtime.js +531 -0
- package/dist/src/cluster_v2/runtime.lease-compromise.integration.test.js +91 -0
- package/dist/src/cluster_v2/runtime.lifecycle.integration.test.js +225 -0
- package/dist/src/cluster_v2/services/bus.integration.test.js +140 -0
- package/dist/src/cluster_v2/services/bus.js +450 -0
- package/dist/src/cluster_v2/services/discord/auth-store.js +82 -0
- package/dist/src/cluster_v2/services/discord/collector.js +569 -0
- package/dist/src/cluster_v2/services/discord/index.js +1 -0
- package/dist/src/cluster_v2/services/discord/oauth.js +87 -0
- package/dist/src/cluster_v2/services/discord/rpc-client.js +325 -0
- package/dist/src/cluster_v2/services/embedding.js +66 -0
- package/dist/src/cluster_v2/services/registry-cache.js +107 -0
- package/dist/src/cluster_v2/services/registry-cache.test.js +66 -0
- package/dist/src/cluster_v2/services/registry.js +36 -0
- package/dist/src/cluster_v2/services/twitter/collector.js +1055 -0
- package/dist/src/cluster_v2/services/twitter/index.js +1 -0
- package/dist/src/config/digest.js +78 -0
- package/dist/src/config/discord.js +143 -0
- package/dist/src/config/image-gen.js +48 -0
- package/dist/src/config/mono-pilot.js +31 -0
- package/dist/src/config/twitter.js +100 -0
- package/dist/src/extensions/cluster.js +311 -0
- package/dist/src/extensions/commands/build-memory.js +76 -0
- package/dist/src/extensions/commands/digest/backfill.js +779 -0
- package/dist/src/extensions/commands/digest/index.js +1133 -0
- package/dist/src/extensions/commands/image-model.js +214 -0
- package/dist/src/extensions/game/bus-injection.js +47 -0
- package/dist/src/extensions/game/identity.js +83 -0
- package/dist/src/extensions/game/mailbox.js +61 -0
- package/dist/src/extensions/game/system-prompt.js +134 -0
- package/dist/src/extensions/game/tools.js +28 -0
- package/dist/src/extensions/lifecycle.js +337 -0
- package/dist/src/extensions/mode-runtime.js +26 -2
- package/dist/src/extensions/mono-game.js +66 -0
- package/dist/src/extensions/mono-pilot.js +100 -18
- package/dist/src/extensions/nvim.js +47 -0
- package/dist/src/extensions/session-hints.js +1 -2
- package/dist/src/extensions/sftp.js +897 -0
- package/dist/src/extensions/status.js +676 -0
- package/dist/src/extensions/system-events.js +478 -0
- package/dist/src/extensions/system-prompt.js +24 -14
- package/dist/src/extensions/user-message.js +70 -1
- package/dist/src/lsp/client.js +235 -0
- package/dist/src/lsp/index.js +165 -0
- package/dist/src/lsp/runtime.js +67 -0
- package/dist/src/lsp/server.js +242 -0
- package/dist/src/memory/build-memory.js +103 -0
- package/dist/src/memory/config/defaults.js +55 -0
- package/dist/src/memory/config/loader.js +29 -0
- package/dist/src/memory/config/paths.js +9 -0
- package/dist/src/memory/config/resolve.js +90 -0
- package/dist/src/memory/config/types.js +1 -0
- package/dist/src/memory/embeddings/batch-runner.js +39 -0
- package/dist/src/memory/embeddings/cache.js +47 -0
- package/dist/src/memory/embeddings/chunk-limits.js +26 -0
- package/dist/src/memory/embeddings/input-limits.js +48 -0
- package/dist/src/memory/embeddings/local.js +108 -0
- package/dist/src/memory/embeddings/types.js +1 -0
- package/dist/src/memory/index-manager.js +552 -0
- package/dist/src/memory/indexing/embeddings.js +67 -0
- package/dist/src/memory/indexing/files.js +180 -0
- package/dist/src/memory/indexing/index-file.js +105 -0
- package/dist/src/memory/log.js +38 -0
- package/dist/src/memory/paths.js +15 -0
- package/dist/src/memory/runtime/index.js +299 -0
- package/dist/src/memory/runtime/thread.js +116 -0
- package/dist/src/memory/search/fts.js +57 -0
- package/dist/src/memory/search/hybrid.js +50 -0
- package/dist/src/memory/search/text.js +30 -0
- package/dist/src/memory/search/vector.js +43 -0
- package/dist/src/memory/session/content-hash.js +7 -0
- package/dist/src/memory/session/entry.js +33 -0
- package/dist/src/memory/session/flush-policy.js +34 -0
- package/dist/src/memory/session/hook.js +191 -0
- package/dist/src/memory/session/paths.js +15 -0
- package/dist/src/memory/session/session-reader.js +88 -0
- package/dist/src/memory/session/transcript/content-hash.js +7 -0
- package/dist/src/memory/session/transcript/entry.js +28 -0
- package/dist/src/memory/session/transcript/flush.js +56 -0
- package/dist/src/memory/session/transcript/paths.js +28 -0
- package/dist/src/memory/session/transcript/reader.js +112 -0
- package/dist/src/memory/session/transcript/state.js +31 -0
- package/dist/src/memory/store/schema.js +89 -0
- package/dist/src/memory/store/sqlite.js +89 -0
- package/dist/src/memory/types.js +1 -0
- package/dist/src/memory/warm.js +25 -0
- package/dist/{tools → src/tools}/README.md +28 -2
- package/dist/{tools → src/tools}/apply-patch-description.md +8 -2
- package/dist/{tools → src/tools}/apply-patch.js +174 -104
- package/dist/{tools → src/tools}/apply-patch.test.js +52 -1
- package/dist/{tools/ask-question.js → src/tools/ask-user-question.js} +3 -3
- package/dist/src/tools/ast-grep.js +357 -0
- package/dist/src/tools/brief-write.js +122 -0
- package/dist/src/tools/bus-send.js +100 -0
- package/dist/{tools → src/tools}/call-mcp-tool.js +20 -24
- package/dist/src/tools/codex-apply-patch-description.md +52 -0
- package/dist/src/tools/codex-apply-patch.js +540 -0
- package/dist/{tools → src/tools}/delete.js +24 -0
- package/dist/src/tools/exit-plan-mode.js +83 -0
- package/dist/{tools → src/tools}/fetch-mcp-resource.js +31 -3
- package/dist/src/tools/generate-image.js +567 -0
- package/dist/{tools → src/tools}/glob.js +55 -1
- package/dist/{tools → src/tools}/list-mcp-resources.js +32 -3
- package/dist/{tools → src/tools}/list-mcp-tools.js +38 -3
- package/dist/src/tools/ls.js +48 -0
- package/dist/src/tools/lsp-diagnostics.js +67 -0
- package/dist/src/tools/lsp-symbols.js +54 -0
- package/dist/src/tools/mailbox.js +85 -0
- package/dist/src/tools/memory-get.js +90 -0
- package/dist/src/tools/memory-search.js +180 -0
- package/dist/{tools → src/tools}/plan-mode-reminder.md +3 -4
- package/dist/{tools → src/tools}/read-file.js +8 -19
- package/dist/{tools → src/tools}/rg.js +10 -20
- package/dist/{tools → src/tools}/shell.js +19 -42
- package/dist/{tools → src/tools}/subagent.js +255 -6
- package/dist/{tools → src/tools}/switch-mode.js +37 -6
- package/dist/{tools → src/tools}/web-fetch.js +105 -7
- package/dist/{tools → src/tools}/web-search.js +29 -1
- package/package.json +21 -9
- package/dist/src/utils/mcp-client.js +0 -282
- /package/dist/{tools → src/tools}/ask-mode-reminder.md +0 -0
- /package/dist/{tools → src/tools}/rg.test.js +0 -0
- /package/dist/{tools → src/tools}/semantic-search-description.md +0 -0
- /package/dist/{tools → src/tools}/semantic-search.js +0 -0
- /package/dist/{tools → src/tools}/shell-description.md +0 -0
- /package/dist/{tools → src/tools}/subagent-description.md +0 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { createClusterLogContext, logClusterEvent } from "../observability.js";
|
|
2
|
+
export const BUS_METHOD_REGISTER = "bus.register";
|
|
3
|
+
export const BUS_METHOD_SUBSCRIBE = "bus.subscribe";
|
|
4
|
+
export const BUS_METHOD_SEND = "bus.send";
|
|
5
|
+
export const BUS_METHOD_BROADCAST = "bus.broadcast";
|
|
6
|
+
export const BUS_METHOD_ROSTER = "bus.roster";
|
|
7
|
+
const CONNECTION_AGENT_ID_KEY = "bus.agentId";
|
|
8
|
+
function parseRegisterParams(params) {
|
|
9
|
+
if (!params || typeof params !== "object") {
|
|
10
|
+
throw new Error("bus.register requires object params");
|
|
11
|
+
}
|
|
12
|
+
const p = params;
|
|
13
|
+
if (!p.agentId || typeof p.agentId !== "string") {
|
|
14
|
+
throw new Error("bus.register requires string agentId");
|
|
15
|
+
}
|
|
16
|
+
if (p.channels && (!Array.isArray(p.channels) || p.channels.some((c) => typeof c !== "string"))) {
|
|
17
|
+
throw new Error("bus.register channels must be string[]");
|
|
18
|
+
}
|
|
19
|
+
if (p.displayName !== undefined && typeof p.displayName !== "string") {
|
|
20
|
+
throw new Error("bus.register displayName must be string");
|
|
21
|
+
}
|
|
22
|
+
return p;
|
|
23
|
+
}
|
|
24
|
+
function parseSubscribeParams(params) {
|
|
25
|
+
if (!params || typeof params !== "object") {
|
|
26
|
+
throw new Error("bus.subscribe requires object params");
|
|
27
|
+
}
|
|
28
|
+
const p = params;
|
|
29
|
+
if (!Array.isArray(p.channels) || p.channels.some((c) => typeof c !== "string")) {
|
|
30
|
+
throw new Error("bus.subscribe requires string[] channels");
|
|
31
|
+
}
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
function parseSendParams(params) {
|
|
35
|
+
if (!params || typeof params !== "object") {
|
|
36
|
+
throw new Error("bus.send requires object params");
|
|
37
|
+
}
|
|
38
|
+
const p = params;
|
|
39
|
+
if (!p.to || typeof p.to !== "string") {
|
|
40
|
+
throw new Error("bus.send requires string to");
|
|
41
|
+
}
|
|
42
|
+
if (p.channel !== undefined && typeof p.channel !== "string") {
|
|
43
|
+
throw new Error("bus.send channel must be string");
|
|
44
|
+
}
|
|
45
|
+
return p;
|
|
46
|
+
}
|
|
47
|
+
function parseBroadcastParams(params) {
|
|
48
|
+
if (!params || typeof params !== "object") {
|
|
49
|
+
throw new Error("bus.broadcast requires object params");
|
|
50
|
+
}
|
|
51
|
+
const p = params;
|
|
52
|
+
if (p.channel !== undefined && typeof p.channel !== "string") {
|
|
53
|
+
throw new Error("bus.broadcast channel must be string");
|
|
54
|
+
}
|
|
55
|
+
return p;
|
|
56
|
+
}
|
|
57
|
+
export function createBusService() {
|
|
58
|
+
const agents = new Map();
|
|
59
|
+
let messageSeq = 0;
|
|
60
|
+
const getDefaultChannels = (agentId) => ["public", `private:${agentId}`];
|
|
61
|
+
const broadcastPresence = (agentId, status, exclude) => {
|
|
62
|
+
const payload = {
|
|
63
|
+
agentId,
|
|
64
|
+
displayName: agents.get(agentId)?.displayName,
|
|
65
|
+
status,
|
|
66
|
+
};
|
|
67
|
+
for (const [id, agent] of agents) {
|
|
68
|
+
if (id === exclude)
|
|
69
|
+
continue;
|
|
70
|
+
agent.push("presence", payload);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const registerAgent = (agentId, push, role, channels, displayName, connection) => {
|
|
74
|
+
const existing = agents.get(agentId);
|
|
75
|
+
const previousRole = existing?.role ?? null;
|
|
76
|
+
const roleDowngradePrevented = existing?.role === "leader" && role === "follower";
|
|
77
|
+
if (existing && roleDowngradePrevented) {
|
|
78
|
+
const allChannels = [...new Set([...existing.channels, ...getDefaultChannels(agentId), ...(channels ?? [])])];
|
|
79
|
+
agents.set(agentId, {
|
|
80
|
+
agentId,
|
|
81
|
+
displayName: existing.displayName ?? displayName,
|
|
82
|
+
role: existing.role,
|
|
83
|
+
push: existing.push,
|
|
84
|
+
channels: new Set(allChannels),
|
|
85
|
+
connection: existing.connection,
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
channels: allChannels,
|
|
89
|
+
previousRole,
|
|
90
|
+
nextRole: existing.role,
|
|
91
|
+
roleDowngradePrevented,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (existing) {
|
|
95
|
+
existing.push = () => {
|
|
96
|
+
// old connection is superseded by the new registration.
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const allChannels = [...new Set([...getDefaultChannels(agentId), ...(channels ?? [])])];
|
|
100
|
+
agents.set(agentId, {
|
|
101
|
+
agentId,
|
|
102
|
+
displayName,
|
|
103
|
+
role,
|
|
104
|
+
push,
|
|
105
|
+
channels: new Set(allChannels),
|
|
106
|
+
connection,
|
|
107
|
+
});
|
|
108
|
+
broadcastPresence(agentId, "joined", agentId);
|
|
109
|
+
for (const [id, current] of agents) {
|
|
110
|
+
if (id === agentId)
|
|
111
|
+
continue;
|
|
112
|
+
push("presence", {
|
|
113
|
+
agentId: id,
|
|
114
|
+
displayName: current.displayName,
|
|
115
|
+
status: "joined",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
channels: allChannels,
|
|
120
|
+
previousRole,
|
|
121
|
+
nextRole: role,
|
|
122
|
+
roleDowngradePrevented,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
const unregisterAgent = (agentId) => {
|
|
126
|
+
if (!agents.has(agentId))
|
|
127
|
+
return;
|
|
128
|
+
agents.delete(agentId);
|
|
129
|
+
broadcastPresence(agentId, "left");
|
|
130
|
+
};
|
|
131
|
+
const senderFromConnection = (connection) => {
|
|
132
|
+
const sender = connection.state.get(CONNECTION_AGENT_ID_KEY);
|
|
133
|
+
if (!sender || typeof sender !== "string") {
|
|
134
|
+
throw new Error("must register before calling bus method");
|
|
135
|
+
}
|
|
136
|
+
return sender;
|
|
137
|
+
};
|
|
138
|
+
const handlers = {
|
|
139
|
+
[BUS_METHOD_REGISTER]: async (request, connection) => {
|
|
140
|
+
const { agentId, channels, displayName } = parseRegisterParams(request.params);
|
|
141
|
+
const registration = registerAgent(agentId, (method, payload) => connection.sendPush(method, payload), "follower", channels, displayName, connection);
|
|
142
|
+
const fromSessionId = request.from?.sessionId;
|
|
143
|
+
logClusterEvent(registration.roleDowngradePrevented ? "warn" : "info", registration.roleDowngradePrevented
|
|
144
|
+
? "bus_register_role_downgrade_prevented"
|
|
145
|
+
: "bus_register", createClusterLogContext({
|
|
146
|
+
agentId,
|
|
147
|
+
sessionId: fromSessionId,
|
|
148
|
+
role: "leader:bus",
|
|
149
|
+
}), {
|
|
150
|
+
previousRole: registration.previousRole,
|
|
151
|
+
nextRole: registration.nextRole,
|
|
152
|
+
requestedRole: "follower",
|
|
153
|
+
fromPid: request.from?.pid ?? null,
|
|
154
|
+
fromSessionId: fromSessionId ?? null,
|
|
155
|
+
fromAgentId: request.from?.agentId ?? null,
|
|
156
|
+
channels: registration.channels,
|
|
157
|
+
});
|
|
158
|
+
connection.state.set(CONNECTION_AGENT_ID_KEY, agentId);
|
|
159
|
+
return { agentId, channels: registration.channels };
|
|
160
|
+
},
|
|
161
|
+
[BUS_METHOD_SUBSCRIBE]: async (request, connection) => {
|
|
162
|
+
const sender = senderFromConnection(connection);
|
|
163
|
+
const { channels } = parseSubscribeParams(request.params);
|
|
164
|
+
const agent = agents.get(sender);
|
|
165
|
+
if (!agent) {
|
|
166
|
+
throw new Error("registered agent not found");
|
|
167
|
+
}
|
|
168
|
+
for (const channel of channels) {
|
|
169
|
+
agent.channels.add(channel);
|
|
170
|
+
}
|
|
171
|
+
return { channels: [...agent.channels] };
|
|
172
|
+
},
|
|
173
|
+
[BUS_METHOD_SEND]: async (request, connection) => {
|
|
174
|
+
const sender = senderFromConnection(connection);
|
|
175
|
+
const { to, channel, payload } = parseSendParams(request.params);
|
|
176
|
+
const target = agents.get(to);
|
|
177
|
+
if (!target) {
|
|
178
|
+
throw new Error(`agent not found: ${to}`);
|
|
179
|
+
}
|
|
180
|
+
const seq = ++messageSeq;
|
|
181
|
+
target.push("message", {
|
|
182
|
+
from: sender,
|
|
183
|
+
fromName: agents.get(sender)?.displayName,
|
|
184
|
+
channel,
|
|
185
|
+
payload,
|
|
186
|
+
seq,
|
|
187
|
+
});
|
|
188
|
+
return { seq };
|
|
189
|
+
},
|
|
190
|
+
[BUS_METHOD_BROADCAST]: async (request, connection) => {
|
|
191
|
+
const sender = senderFromConnection(connection);
|
|
192
|
+
const { channel, payload } = parseBroadcastParams(request.params);
|
|
193
|
+
const targetChannel = channel ?? "public";
|
|
194
|
+
const seq = ++messageSeq;
|
|
195
|
+
let delivered = 0;
|
|
196
|
+
const pushPayload = {
|
|
197
|
+
from: sender,
|
|
198
|
+
fromName: agents.get(sender)?.displayName,
|
|
199
|
+
channel: targetChannel,
|
|
200
|
+
payload,
|
|
201
|
+
seq,
|
|
202
|
+
};
|
|
203
|
+
for (const [id, agent] of agents) {
|
|
204
|
+
if (id === sender)
|
|
205
|
+
continue;
|
|
206
|
+
if (agent.channels.has(targetChannel)) {
|
|
207
|
+
agent.push("message", pushPayload);
|
|
208
|
+
delivered++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return { seq, delivered };
|
|
212
|
+
},
|
|
213
|
+
[BUS_METHOD_ROSTER]: async () => {
|
|
214
|
+
return {
|
|
215
|
+
agents: [...agents.entries()].map(([id, agent]) => ({
|
|
216
|
+
agentId: id,
|
|
217
|
+
displayName: agent.displayName,
|
|
218
|
+
role: agent.role,
|
|
219
|
+
channels: [...agent.channels],
|
|
220
|
+
})),
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
const onConnectionClosed = (connection) => {
|
|
225
|
+
const agentId = connection.state.get(CONNECTION_AGENT_ID_KEY);
|
|
226
|
+
if (!agentId || typeof agentId !== "string") {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const current = agents.get(agentId);
|
|
230
|
+
if (current?.connection !== connection) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
unregisterAgent(agentId);
|
|
234
|
+
};
|
|
235
|
+
const createLeaderHandle = (agentId, displayName) => {
|
|
236
|
+
let closed = false;
|
|
237
|
+
let messageHandlers = [];
|
|
238
|
+
let presenceHandlers = [];
|
|
239
|
+
const push = (method, payload) => {
|
|
240
|
+
if (closed)
|
|
241
|
+
return;
|
|
242
|
+
if (method === "message") {
|
|
243
|
+
for (const handler of messageHandlers) {
|
|
244
|
+
handler(payload);
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (method === "presence") {
|
|
249
|
+
for (const handler of presenceHandlers) {
|
|
250
|
+
handler(payload);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
registerAgent(agentId, push, "leader", undefined, displayName, undefined);
|
|
255
|
+
return {
|
|
256
|
+
async send(to, payload, channel) {
|
|
257
|
+
const target = agents.get(to);
|
|
258
|
+
if (!target) {
|
|
259
|
+
throw new Error(`agent not found: ${to}`);
|
|
260
|
+
}
|
|
261
|
+
const seq = ++messageSeq;
|
|
262
|
+
target.push("message", {
|
|
263
|
+
from: agentId,
|
|
264
|
+
fromName: agents.get(agentId)?.displayName,
|
|
265
|
+
channel,
|
|
266
|
+
payload,
|
|
267
|
+
seq,
|
|
268
|
+
});
|
|
269
|
+
return { seq };
|
|
270
|
+
},
|
|
271
|
+
async broadcast(payload, channel) {
|
|
272
|
+
const targetChannel = channel ?? "public";
|
|
273
|
+
const seq = ++messageSeq;
|
|
274
|
+
let delivered = 0;
|
|
275
|
+
const pushPayload = {
|
|
276
|
+
from: agentId,
|
|
277
|
+
fromName: agents.get(agentId)?.displayName,
|
|
278
|
+
channel: targetChannel,
|
|
279
|
+
payload,
|
|
280
|
+
seq,
|
|
281
|
+
};
|
|
282
|
+
for (const [id, agent] of agents) {
|
|
283
|
+
if (id === agentId)
|
|
284
|
+
continue;
|
|
285
|
+
if (agent.channels.has(targetChannel)) {
|
|
286
|
+
agent.push("message", pushPayload);
|
|
287
|
+
delivered++;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { seq, delivered };
|
|
291
|
+
},
|
|
292
|
+
async subscribe(channels) {
|
|
293
|
+
const agent = agents.get(agentId);
|
|
294
|
+
if (!agent)
|
|
295
|
+
return { channels: [] };
|
|
296
|
+
for (const channel of channels) {
|
|
297
|
+
agent.channels.add(channel);
|
|
298
|
+
}
|
|
299
|
+
return { channels: [...agent.channels] };
|
|
300
|
+
},
|
|
301
|
+
async roster() {
|
|
302
|
+
return {
|
|
303
|
+
agents: [...agents.entries()].map(([id, agent]) => ({
|
|
304
|
+
agentId: id,
|
|
305
|
+
displayName: agent.displayName,
|
|
306
|
+
role: agent.role,
|
|
307
|
+
channels: [...agent.channels],
|
|
308
|
+
})),
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
async resolveTarget(target) {
|
|
312
|
+
const { agents: roster } = await this.roster();
|
|
313
|
+
const byId = roster.find((agent) => agent.agentId === target);
|
|
314
|
+
if (byId)
|
|
315
|
+
return { agentId: byId.agentId, displayName: byId.displayName };
|
|
316
|
+
const matches = roster.filter((agent) => agent.displayName?.trim() && agent.displayName.trim() === target);
|
|
317
|
+
if (matches.length === 1) {
|
|
318
|
+
return { agentId: matches[0].agentId, displayName: matches[0].displayName };
|
|
319
|
+
}
|
|
320
|
+
if (matches.length === 0) {
|
|
321
|
+
throw new Error(`No agent found for "${target}".`);
|
|
322
|
+
}
|
|
323
|
+
const ids = matches.map((agent) => agent.agentId).join(", ");
|
|
324
|
+
throw new Error(`DisplayName "${target}" is not unique. Candidates: ${ids}.`);
|
|
325
|
+
},
|
|
326
|
+
onMessage(handler) {
|
|
327
|
+
messageHandlers.push(handler);
|
|
328
|
+
},
|
|
329
|
+
onPresence(handler) {
|
|
330
|
+
presenceHandlers.push(handler);
|
|
331
|
+
},
|
|
332
|
+
close() {
|
|
333
|
+
if (closed)
|
|
334
|
+
return;
|
|
335
|
+
closed = true;
|
|
336
|
+
messageHandlers = [];
|
|
337
|
+
presenceHandlers = [];
|
|
338
|
+
unregisterAgent(agentId);
|
|
339
|
+
},
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
return {
|
|
343
|
+
handlers,
|
|
344
|
+
onConnectionClosed,
|
|
345
|
+
createLeaderHandle,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
export async function connectBusClient(client, agentId, displayName, channels) {
|
|
349
|
+
let closed = false;
|
|
350
|
+
let buffering = true;
|
|
351
|
+
const eventBuffer = [];
|
|
352
|
+
let messageHandlers = [];
|
|
353
|
+
let presenceHandlers = [];
|
|
354
|
+
const dispatch = (method, payload) => {
|
|
355
|
+
if (method === "message") {
|
|
356
|
+
for (const handler of messageHandlers) {
|
|
357
|
+
handler(payload);
|
|
358
|
+
}
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (method === "presence") {
|
|
362
|
+
for (const handler of presenceHandlers) {
|
|
363
|
+
handler(payload);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
const unbindMessage = client.onPush("message", (payload) => {
|
|
368
|
+
if (closed)
|
|
369
|
+
return;
|
|
370
|
+
if (buffering) {
|
|
371
|
+
eventBuffer.push({ method: "message", payload });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
dispatch("message", payload);
|
|
375
|
+
});
|
|
376
|
+
const unbindPresence = client.onPush("presence", (payload) => {
|
|
377
|
+
if (closed)
|
|
378
|
+
return;
|
|
379
|
+
if (buffering) {
|
|
380
|
+
eventBuffer.push({ method: "presence", payload });
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
dispatch("presence", payload);
|
|
384
|
+
});
|
|
385
|
+
await client.call(BUS_METHOD_REGISTER, {
|
|
386
|
+
agentId,
|
|
387
|
+
displayName,
|
|
388
|
+
channels,
|
|
389
|
+
});
|
|
390
|
+
process.nextTick(() => {
|
|
391
|
+
buffering = false;
|
|
392
|
+
for (const evt of eventBuffer) {
|
|
393
|
+
dispatch(evt.method, evt.payload);
|
|
394
|
+
}
|
|
395
|
+
eventBuffer.length = 0;
|
|
396
|
+
});
|
|
397
|
+
const roster = async () => {
|
|
398
|
+
return client.call(BUS_METHOD_ROSTER, {});
|
|
399
|
+
};
|
|
400
|
+
return {
|
|
401
|
+
async send(to, payload, channel) {
|
|
402
|
+
return client.call(BUS_METHOD_SEND, {
|
|
403
|
+
to,
|
|
404
|
+
channel,
|
|
405
|
+
payload,
|
|
406
|
+
});
|
|
407
|
+
},
|
|
408
|
+
async broadcast(payload, channel) {
|
|
409
|
+
return client.call(BUS_METHOD_BROADCAST, {
|
|
410
|
+
channel,
|
|
411
|
+
payload,
|
|
412
|
+
});
|
|
413
|
+
},
|
|
414
|
+
async subscribe(chs) {
|
|
415
|
+
return client.call(BUS_METHOD_SUBSCRIBE, { channels: chs });
|
|
416
|
+
},
|
|
417
|
+
roster,
|
|
418
|
+
async resolveTarget(target) {
|
|
419
|
+
const { agents } = await roster();
|
|
420
|
+
const byId = agents.find((agent) => agent.agentId === target);
|
|
421
|
+
if (byId)
|
|
422
|
+
return { agentId: byId.agentId, displayName: byId.displayName };
|
|
423
|
+
const matches = agents.filter((agent) => agent.displayName?.trim() && agent.displayName.trim() === target);
|
|
424
|
+
if (matches.length === 1) {
|
|
425
|
+
return { agentId: matches[0].agentId, displayName: matches[0].displayName };
|
|
426
|
+
}
|
|
427
|
+
if (matches.length === 0) {
|
|
428
|
+
throw new Error(`No agent found for "${target}".`);
|
|
429
|
+
}
|
|
430
|
+
const ids = matches.map((agent) => agent.agentId).join(", ");
|
|
431
|
+
throw new Error(`DisplayName "${target}" is not unique. Candidates: ${ids}.`);
|
|
432
|
+
},
|
|
433
|
+
onMessage(handler) {
|
|
434
|
+
messageHandlers.push(handler);
|
|
435
|
+
},
|
|
436
|
+
onPresence(handler) {
|
|
437
|
+
presenceHandlers.push(handler);
|
|
438
|
+
},
|
|
439
|
+
close() {
|
|
440
|
+
if (closed)
|
|
441
|
+
return;
|
|
442
|
+
closed = true;
|
|
443
|
+
messageHandlers = [];
|
|
444
|
+
presenceHandlers = [];
|
|
445
|
+
eventBuffer.length = 0;
|
|
446
|
+
unbindMessage();
|
|
447
|
+
unbindPresence();
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
const AUTH_PATH = join(homedir(), ".mono-pilot", "auth.json");
|
|
6
|
+
function isRecord(value) {
|
|
7
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function readString(value) {
|
|
10
|
+
if (typeof value !== "string") {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const trimmed = value.trim();
|
|
14
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
15
|
+
}
|
|
16
|
+
async function loadAuthObject() {
|
|
17
|
+
if (!existsSync(AUTH_PATH)) {
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
const raw = await readFile(AUTH_PATH, "utf-8");
|
|
21
|
+
let parsed;
|
|
22
|
+
try {
|
|
23
|
+
parsed = JSON.parse(raw);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
+
throw new Error(`Invalid JSON in auth store: ${message}`);
|
|
28
|
+
}
|
|
29
|
+
if (!isRecord(parsed)) {
|
|
30
|
+
throw new Error("Auth store root must be a JSON object.");
|
|
31
|
+
}
|
|
32
|
+
return parsed;
|
|
33
|
+
}
|
|
34
|
+
function normalizeToken(value) {
|
|
35
|
+
const accessToken = readString(value.accessToken);
|
|
36
|
+
if (!accessToken) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
accessToken,
|
|
41
|
+
refreshToken: readString(value.refreshToken),
|
|
42
|
+
tokenType: readString(value.tokenType),
|
|
43
|
+
scope: readString(value.scope),
|
|
44
|
+
expiresAt: readString(value.expiresAt),
|
|
45
|
+
updatedAt: readString(value.updatedAt) ?? new Date(0).toISOString(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function getAuthStorePath() {
|
|
49
|
+
return AUTH_PATH;
|
|
50
|
+
}
|
|
51
|
+
export async function readDiscordAuthToken(clientId) {
|
|
52
|
+
const root = await loadAuthObject();
|
|
53
|
+
const discord = isRecord(root.discord) ? root.discord : null;
|
|
54
|
+
const tokens = discord && isRecord(discord.tokens) ? discord.tokens : null;
|
|
55
|
+
if (!tokens) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const raw = tokens[clientId];
|
|
59
|
+
if (!isRecord(raw)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return normalizeToken(raw);
|
|
63
|
+
}
|
|
64
|
+
export async function writeDiscordAuthToken(clientId, token) {
|
|
65
|
+
const root = await loadAuthObject();
|
|
66
|
+
const discord = isRecord(root.discord) ? { ...root.discord } : {};
|
|
67
|
+
const tokens = isRecord(discord.tokens) ? { ...discord.tokens } : {};
|
|
68
|
+
const normalized = {
|
|
69
|
+
accessToken: token.accessToken,
|
|
70
|
+
refreshToken: token.refreshToken,
|
|
71
|
+
tokenType: token.tokenType,
|
|
72
|
+
scope: token.scope,
|
|
73
|
+
expiresAt: token.expiresAt,
|
|
74
|
+
updatedAt: new Date().toISOString(),
|
|
75
|
+
};
|
|
76
|
+
tokens[clientId] = normalized;
|
|
77
|
+
discord.tokens = tokens;
|
|
78
|
+
root.discord = discord;
|
|
79
|
+
await mkdir(dirname(AUTH_PATH), { recursive: true });
|
|
80
|
+
await writeFile(AUTH_PATH, `${JSON.stringify(root, null, 2)}\n`, "utf-8");
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|