@selesai/code 0.1.4 → 0.1.6
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/dist/config.d.ts +21 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +71 -4
- package/dist/config.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +6 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/extensions/pi-intercom/LICENSE +21 -0
- package/dist/extensions/pi-intercom/README.md +484 -0
- package/dist/extensions/pi-intercom/broker/broker.ts +345 -0
- package/dist/extensions/pi-intercom/broker/client.ts +535 -0
- package/dist/extensions/pi-intercom/broker/framing.ts +57 -0
- package/dist/extensions/pi-intercom/broker/paths.ts +20 -0
- package/dist/extensions/pi-intercom/broker/spawn.ts +307 -0
- package/dist/extensions/pi-intercom/config.ts +108 -0
- package/dist/extensions/pi-intercom/index.ts +1778 -0
- package/dist/extensions/pi-intercom/package.json +38 -0
- package/dist/extensions/pi-intercom/reply-tracker.ts +102 -0
- package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +512 -0
- package/dist/extensions/pi-intercom/types.ts +46 -0
- package/dist/extensions/pi-intercom/ui/compose.ts +139 -0
- package/dist/extensions/pi-intercom/ui/inline-message.ts +76 -0
- package/dist/extensions/pi-intercom/ui/session-list.ts +162 -0
- package/dist/extensions/pi-rewind-hook/README.md +245 -0
- package/dist/extensions/pi-rewind-hook/index.ts +1445 -0
- package/dist/extensions/pi-rewind-hook/package.json +29 -0
- package/dist/extensions/pi-subagents/README.md +1290 -0
- package/dist/extensions/pi-subagents/agents/context-builder.md +46 -0
- package/dist/extensions/pi-subagents/agents/delegate.md +12 -0
- package/dist/extensions/pi-subagents/agents/oracle.md +73 -0
- package/dist/extensions/pi-subagents/agents/planner.md +55 -0
- package/dist/extensions/pi-subagents/agents/researcher.md +52 -0
- package/dist/extensions/pi-subagents/agents/reviewer.md +79 -0
- package/dist/extensions/pi-subagents/agents/scout.md +50 -0
- package/dist/extensions/pi-subagents/agents/worker.md +55 -0
- package/dist/extensions/pi-subagents/package.json +83 -0
- package/dist/extensions/pi-subagents/prompts/gather-context-and-clarify.md +13 -0
- package/dist/extensions/pi-subagents/prompts/parallel-cleanup.md +59 -0
- package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +55 -0
- package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +61 -0
- package/dist/extensions/pi-subagents/prompts/parallel-research.md +50 -0
- package/dist/extensions/pi-subagents/prompts/parallel-review.md +54 -0
- package/dist/extensions/pi-subagents/prompts/review-loop.md +41 -0
- package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +855 -0
- package/dist/extensions/pi-subagents/src/agents/agent-management.ts +875 -0
- package/dist/extensions/pi-subagents/src/agents/agent-scope.ts +6 -0
- package/dist/extensions/pi-subagents/src/agents/agent-selection.ts +25 -0
- package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +110 -0
- package/dist/extensions/pi-subagents/src/agents/agents.ts +1379 -0
- package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +252 -0
- package/dist/extensions/pi-subagents/src/agents/frontmatter.ts +93 -0
- package/dist/extensions/pi-subagents/src/agents/identity.ts +30 -0
- package/dist/extensions/pi-subagents/src/agents/proactive-skills.ts +191 -0
- package/dist/extensions/pi-subagents/src/agents/skills.ts +729 -0
- package/dist/extensions/pi-subagents/src/extension/config.ts +16 -0
- package/dist/extensions/pi-subagents/src/extension/control-notices.ts +92 -0
- package/dist/extensions/pi-subagents/src/extension/doctor.ts +220 -0
- package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +171 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +627 -0
- package/dist/extensions/pi-subagents/src/extension/schemas.ts +265 -0
- package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +412 -0
- package/dist/extensions/pi-subagents/src/intercom/result-intercom.ts +377 -0
- package/dist/extensions/pi-subagents/src/profiles/profiles.ts +637 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +916 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +355 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +391 -0
- package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +338 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-append.ts +282 -0
- package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +161 -0
- package/dist/extensions/pi-subagents/src/runs/background/completion-dedupe.ts +63 -0
- package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +177 -0
- package/dist/extensions/pi-subagents/src/runs/background/notify.ts +108 -0
- package/dist/extensions/pi-subagents/src/runs/background/parallel-groups.ts +45 -0
- package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +316 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +83 -0
- package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +273 -0
- package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +341 -0
- package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +2458 -0
- package/dist/extensions/pi-subagents/src/runs/background/top-level-async.ts +13 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-clarify.ts +1333 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +1217 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +1092 -0
- package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +3125 -0
- package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +856 -0
- package/dist/extensions/pi-subagents/src/runs/shared/chain-outputs.ts +116 -0
- package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +147 -0
- package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +295 -0
- package/dist/extensions/pi-subagents/src/runs/shared/long-running-guard.ts +175 -0
- package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +365 -0
- package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +141 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +819 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +52 -0
- package/dist/extensions/pi-subagents/src/runs/shared/nested-render.ts +115 -0
- package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +149 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +240 -0
- package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +115 -0
- package/dist/extensions/pi-subagents/src/runs/shared/run-history.ts +60 -0
- package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +178 -0
- package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +77 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +223 -0
- package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +222 -0
- package/dist/extensions/pi-subagents/src/runs/shared/workflow-graph.ts +206 -0
- package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +577 -0
- package/dist/extensions/pi-subagents/src/shared/artifacts.ts +98 -0
- package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +73 -0
- package/dist/extensions/pi-subagents/src/shared/file-coalescer.ts +40 -0
- package/dist/extensions/pi-subagents/src/shared/fork-context.ts +76 -0
- package/dist/extensions/pi-subagents/src/shared/formatters.ts +133 -0
- package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +81 -0
- package/dist/extensions/pi-subagents/src/shared/model-info.ts +78 -0
- package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +85 -0
- package/dist/extensions/pi-subagents/src/shared/session-identity.ts +10 -0
- package/dist/extensions/pi-subagents/src/shared/session-tokens.ts +44 -0
- package/dist/extensions/pi-subagents/src/shared/settings.ts +448 -0
- package/dist/extensions/pi-subagents/src/shared/status-format.ts +49 -0
- package/dist/extensions/pi-subagents/src/shared/types.ts +1064 -0
- package/dist/extensions/pi-subagents/src/shared/utils.ts +469 -0
- package/dist/extensions/pi-subagents/src/slash/prompt-template-bridge.ts +420 -0
- package/dist/extensions/pi-subagents/src/slash/slash-bridge.ts +176 -0
- package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +1165 -0
- package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +292 -0
- package/dist/extensions/pi-subagents/src/tui/render-helpers.ts +80 -0
- package/dist/extensions/pi-subagents/src/tui/render.ts +1728 -0
- package/dist/extensions/quick.ts +630 -0
- package/dist/modes/index.d.ts +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +4 -3
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +3 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import net from "net";
|
|
2
|
+
import { writeFileSync, unlinkSync, mkdirSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
import { writeMessage, createMessageReader } from "./framing.js";
|
|
7
|
+
import { getBrokerSocketPath } from "./paths.js";
|
|
8
|
+
import type { SessionInfo, Message, Attachment, BrokerMessage } from "../types.js";
|
|
9
|
+
|
|
10
|
+
const INTERCOM_DIR = join(homedir(), ".pi/agent/intercom");
|
|
11
|
+
const SOCKET_PATH = getBrokerSocketPath();
|
|
12
|
+
const PID_PATH = join(INTERCOM_DIR, "broker.pid");
|
|
13
|
+
|
|
14
|
+
interface ConnectedSession {
|
|
15
|
+
socket: net.Socket;
|
|
16
|
+
info: SessionInfo;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isAttachment(value: unknown): value is Attachment {
|
|
20
|
+
if (typeof value !== "object" || value === null) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const attachment = value as Record<string, unknown>;
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
attachment.type !== "file"
|
|
28
|
+
&& attachment.type !== "snippet"
|
|
29
|
+
&& attachment.type !== "context"
|
|
30
|
+
) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof attachment.name !== "string" || typeof attachment.content !== "string") {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return attachment.language === undefined || typeof attachment.language === "string";
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isMessage(value: unknown): value is Message {
|
|
42
|
+
if (typeof value !== "object" || value === null) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const message = value as Record<string, unknown>;
|
|
47
|
+
|
|
48
|
+
if (typeof message.id !== "string" || typeof message.timestamp !== "number") {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (message.replyTo !== undefined && typeof message.replyTo !== "string") {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (message.expectsReply !== undefined && typeof message.expectsReply !== "boolean") {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (typeof message.content !== "object" || message.content === null) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const content = message.content as Record<string, unknown>;
|
|
65
|
+
if (typeof content.text !== "string") {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return content.attachments === undefined
|
|
70
|
+
|| (Array.isArray(content.attachments) && content.attachments.every(isAttachment));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isSessionRegistration(value: unknown): value is Omit<SessionInfo, "id"> {
|
|
74
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const session = value as Record<string, unknown>;
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
typeof session.cwd !== "string"
|
|
82
|
+
|| typeof session.model !== "string"
|
|
83
|
+
|| typeof session.pid !== "number"
|
|
84
|
+
|| typeof session.startedAt !== "number"
|
|
85
|
+
|| typeof session.lastActivity !== "number"
|
|
86
|
+
) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (session.name !== undefined && typeof session.name !== "string") {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return session.status === undefined || typeof session.status === "string";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
class IntercomBroker {
|
|
98
|
+
private sessions = new Map<string, ConnectedSession>();
|
|
99
|
+
private server: net.Server;
|
|
100
|
+
private shutdownTimer: NodeJS.Timeout | null = null;
|
|
101
|
+
|
|
102
|
+
constructor() {
|
|
103
|
+
mkdirSync(INTERCOM_DIR, { recursive: true });
|
|
104
|
+
if (process.platform !== "win32") {
|
|
105
|
+
try {
|
|
106
|
+
unlinkSync(SOCKET_PATH);
|
|
107
|
+
} catch {
|
|
108
|
+
// A clean startup has no stale socket to remove.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.server = net.createServer(this.handleConnection.bind(this));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
start(): void {
|
|
115
|
+
this.server.listen(SOCKET_PATH, () => {
|
|
116
|
+
writeFileSync(PID_PATH, String(process.pid));
|
|
117
|
+
console.log(`Intercom broker started (pid: ${process.pid})`);
|
|
118
|
+
});
|
|
119
|
+
process.on("SIGTERM", () => this.shutdown());
|
|
120
|
+
process.on("SIGINT", () => this.shutdown());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private handleConnection(socket: net.Socket): void {
|
|
124
|
+
let sessionId: string | null = null;
|
|
125
|
+
|
|
126
|
+
const reader = createMessageReader((msg) => {
|
|
127
|
+
this.handleMessage(socket, msg, sessionId, (id) => {
|
|
128
|
+
sessionId = id;
|
|
129
|
+
});
|
|
130
|
+
}, (error) => {
|
|
131
|
+
socket.destroy(error);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
socket.on("data", reader);
|
|
135
|
+
|
|
136
|
+
socket.on("close", () => {
|
|
137
|
+
if (sessionId) {
|
|
138
|
+
this.sessions.delete(sessionId);
|
|
139
|
+
this.broadcast({ type: "session_left", sessionId }, sessionId);
|
|
140
|
+
|
|
141
|
+
this.scheduleShutdownCheck();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
socket.on("error", (error) => {
|
|
146
|
+
console.error("Socket error:", error);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private scheduleShutdownCheck(): void {
|
|
151
|
+
if (this.shutdownTimer) return;
|
|
152
|
+
|
|
153
|
+
this.shutdownTimer = setTimeout(() => {
|
|
154
|
+
this.shutdownTimer = null;
|
|
155
|
+
if (this.sessions.size === 0) {
|
|
156
|
+
console.log("No sessions connected, shutting down");
|
|
157
|
+
this.shutdown();
|
|
158
|
+
}
|
|
159
|
+
}, 5000);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private handleMessage(
|
|
163
|
+
socket: net.Socket,
|
|
164
|
+
msg: unknown,
|
|
165
|
+
currentId: string | null,
|
|
166
|
+
setId: (id: string | null) => void,
|
|
167
|
+
): void {
|
|
168
|
+
if (typeof msg !== "object" || msg === null || !("type" in msg) || typeof msg.type !== "string") {
|
|
169
|
+
throw new Error("Invalid client message");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const clientMessage = msg as { type: string } & Record<string, unknown>;
|
|
173
|
+
|
|
174
|
+
if (currentId === null && clientMessage.type !== "register") {
|
|
175
|
+
throw new Error(`Received ${clientMessage.type} before register`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
switch (clientMessage.type) {
|
|
179
|
+
case "register": {
|
|
180
|
+
if (!isSessionRegistration(clientMessage.session)) {
|
|
181
|
+
throw new Error("Invalid register message");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (currentId) {
|
|
185
|
+
throw new Error("Received duplicate register message");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const id = randomUUID();
|
|
189
|
+
setId(id);
|
|
190
|
+
const info: SessionInfo = { ...clientMessage.session, id };
|
|
191
|
+
this.sessions.set(id, { socket, info });
|
|
192
|
+
|
|
193
|
+
if (this.shutdownTimer) {
|
|
194
|
+
clearTimeout(this.shutdownTimer);
|
|
195
|
+
this.shutdownTimer = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
writeMessage(socket, { type: "registered", sessionId: id });
|
|
199
|
+
this.broadcast({ type: "session_joined", session: info }, id);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case "unregister": {
|
|
204
|
+
this.sessions.delete(currentId);
|
|
205
|
+
this.broadcast({ type: "session_left", sessionId: currentId }, currentId);
|
|
206
|
+
setId(null);
|
|
207
|
+
this.scheduleShutdownCheck();
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
case "list": {
|
|
212
|
+
if (typeof clientMessage.requestId !== "string") {
|
|
213
|
+
throw new Error("Invalid list message");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const sessions = Array.from(this.sessions.values()).map(s => s.info);
|
|
217
|
+
writeMessage(socket, { type: "sessions", requestId: clientMessage.requestId, sessions });
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
case "send": {
|
|
222
|
+
const message = clientMessage.message;
|
|
223
|
+
const messageId = isMessage(message) ? message.id : "unknown";
|
|
224
|
+
|
|
225
|
+
if (typeof clientMessage.to !== "string" || !isMessage(message)) {
|
|
226
|
+
writeMessage(socket, {
|
|
227
|
+
type: "delivery_failed",
|
|
228
|
+
messageId,
|
|
229
|
+
reason: "Invalid message format",
|
|
230
|
+
});
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const targets = this.findSessions(clientMessage.to);
|
|
235
|
+
if (targets.length === 1) {
|
|
236
|
+
const fromSession = this.sessions.get(currentId);
|
|
237
|
+
if (!fromSession) {
|
|
238
|
+
writeMessage(socket, {
|
|
239
|
+
type: "delivery_failed",
|
|
240
|
+
messageId: message.id,
|
|
241
|
+
reason: "Sender session not found",
|
|
242
|
+
});
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
writeMessage(targets[0].socket, {
|
|
246
|
+
type: "message",
|
|
247
|
+
from: fromSession.info,
|
|
248
|
+
message,
|
|
249
|
+
});
|
|
250
|
+
writeMessage(socket, { type: "delivered", messageId: message.id });
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (targets.length > 1) {
|
|
255
|
+
writeMessage(socket, {
|
|
256
|
+
type: "delivery_failed",
|
|
257
|
+
messageId: message.id,
|
|
258
|
+
reason: `Multiple sessions named \"${clientMessage.to}\" are connected. Use the session ID instead.`,
|
|
259
|
+
});
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
writeMessage(socket, {
|
|
264
|
+
type: "delivery_failed",
|
|
265
|
+
messageId: message.id,
|
|
266
|
+
reason: "Session not found",
|
|
267
|
+
});
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
case "presence": {
|
|
272
|
+
const session = this.sessions.get(currentId);
|
|
273
|
+
if (session) {
|
|
274
|
+
if (clientMessage.name !== undefined) {
|
|
275
|
+
if (typeof clientMessage.name !== "string") {
|
|
276
|
+
throw new Error("Invalid presence name");
|
|
277
|
+
}
|
|
278
|
+
session.info.name = clientMessage.name;
|
|
279
|
+
}
|
|
280
|
+
if (clientMessage.status !== undefined) {
|
|
281
|
+
if (typeof clientMessage.status !== "string") {
|
|
282
|
+
throw new Error("Invalid presence status");
|
|
283
|
+
}
|
|
284
|
+
session.info.status = clientMessage.status;
|
|
285
|
+
}
|
|
286
|
+
if (clientMessage.model !== undefined) {
|
|
287
|
+
if (typeof clientMessage.model !== "string") {
|
|
288
|
+
throw new Error("Invalid presence model");
|
|
289
|
+
}
|
|
290
|
+
session.info.model = clientMessage.model;
|
|
291
|
+
}
|
|
292
|
+
session.info.lastActivity = Date.now();
|
|
293
|
+
this.broadcast({ type: "presence_update", session: session.info }, currentId);
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
default:
|
|
299
|
+
throw new Error(`Unknown client message type: ${clientMessage.type}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private findSessions(nameOrId: string): ConnectedSession[] {
|
|
304
|
+
const byId = this.sessions.get(nameOrId);
|
|
305
|
+
if (byId) {
|
|
306
|
+
return [byId];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const lowerName = nameOrId.toLowerCase();
|
|
310
|
+
return Array.from(this.sessions.values()).filter(session => session.info.name?.toLowerCase() === lowerName);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
private broadcast(msg: BrokerMessage, exclude?: string): void {
|
|
314
|
+
for (const [id, session] of this.sessions) {
|
|
315
|
+
if (id !== exclude) {
|
|
316
|
+
writeMessage(session.socket, msg);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private shutdown(): void {
|
|
322
|
+
console.log("Broker shutting down");
|
|
323
|
+
|
|
324
|
+
for (const session of this.sessions.values()) {
|
|
325
|
+
session.socket.end();
|
|
326
|
+
}
|
|
327
|
+
this.sessions.clear();
|
|
328
|
+
if (process.platform !== "win32") {
|
|
329
|
+
try {
|
|
330
|
+
unlinkSync(SOCKET_PATH);
|
|
331
|
+
} catch {
|
|
332
|
+
// The socket may already be gone if shutdown started after a disconnect.
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
unlinkSync(PID_PATH);
|
|
337
|
+
} catch {
|
|
338
|
+
// The PID file may already be gone if startup never completed.
|
|
339
|
+
}
|
|
340
|
+
this.server.close();
|
|
341
|
+
process.exit(0);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
new IntercomBroker().start();
|