@dsiloed/silo-link 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +350 -0
- package/dist/api/dsiloed-client.d.ts +32 -0
- package/dist/api/dsiloed-client.d.ts.map +1 -0
- package/dist/api/dsiloed-client.js +240 -0
- package/dist/api/dsiloed-client.js.map +1 -0
- package/dist/cable/action-cable-client.d.ts +28 -0
- package/dist/cable/action-cable-client.d.ts.map +1 -0
- package/dist/cable/action-cable-client.js +194 -0
- package/dist/cable/action-cable-client.js.map +1 -0
- package/dist/cable/subscription-manager.d.ts +29 -0
- package/dist/cable/subscription-manager.d.ts.map +1 -0
- package/dist/cable/subscription-manager.js +125 -0
- package/dist/cable/subscription-manager.js.map +1 -0
- package/dist/cli/commands.d.ts +4 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +144 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/daemon.d.ts +5 -0
- package/dist/cli/daemon.d.ts.map +1 -0
- package/dist/cli/daemon.js +35 -0
- package/dist/cli/daemon.js.map +1 -0
- package/dist/config/config-manager.d.ts +7 -0
- package/dist/config/config-manager.d.ts.map +1 -0
- package/dist/config/config-manager.js +76 -0
- package/dist/config/config-manager.js.map +1 -0
- package/dist/config/jwt-generator.d.ts +15 -0
- package/dist/config/jwt-generator.d.ts.map +1 -0
- package/dist/config/jwt-generator.js +54 -0
- package/dist/config/jwt-generator.js.map +1 -0
- package/dist/core/bridge.d.ts +37 -0
- package/dist/core/bridge.d.ts.map +1 -0
- package/dist/core/bridge.js +247 -0
- package/dist/core/bridge.js.map +1 -0
- package/dist/core/claude-launcher.d.ts +78 -0
- package/dist/core/claude-launcher.d.ts.map +1 -0
- package/dist/core/claude-launcher.js +352 -0
- package/dist/core/claude-launcher.js.map +1 -0
- package/dist/core/message-queue.d.ts +47 -0
- package/dist/core/message-queue.d.ts.map +1 -0
- package/dist/core/message-queue.js +139 -0
- package/dist/core/message-queue.js.map +1 -0
- package/dist/core/session-manager.d.ts +24 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +111 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +27 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +109 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/register-tools.d.ts +19 -0
- package/dist/mcp/tools/register-tools.d.ts.map +1 -0
- package/dist/mcp/tools/register-tools.js +385 -0
- package/dist/mcp/tools/register-tools.js.map +1 -0
- package/dist/types/index.d.ts +74 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { SiloLinkConfig, ConnectionState } from '../types/index.js';
|
|
3
|
+
import type { JwtGenerator } from '../config/jwt-generator.js';
|
|
4
|
+
export declare class ActionCableClient extends EventEmitter {
|
|
5
|
+
private config;
|
|
6
|
+
private jwt;
|
|
7
|
+
private ws;
|
|
8
|
+
private state;
|
|
9
|
+
private reconnectAttempts;
|
|
10
|
+
private reconnectTimer;
|
|
11
|
+
private pendingSubscriptions;
|
|
12
|
+
private confirmedSubscriptions;
|
|
13
|
+
constructor(config: SiloLinkConfig, jwt: JwtGenerator);
|
|
14
|
+
getState(): ConnectionState;
|
|
15
|
+
connect(): void;
|
|
16
|
+
disconnect(): void;
|
|
17
|
+
subscribe(conversationId: number): void;
|
|
18
|
+
unsubscribe(conversationId: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Subscribe to a generic ActionCable channel (not conversation-specific).
|
|
21
|
+
*/
|
|
22
|
+
subscribeChannel(channelName: string, params?: Record<string, unknown>): void;
|
|
23
|
+
private sendSubscribe;
|
|
24
|
+
private handleMessage;
|
|
25
|
+
private scheduleReconnect;
|
|
26
|
+
private clearReconnectTimer;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=action-cable-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-cable-client.d.ts","sourceRoot":"","sources":["../../src/cable/action-cable-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAU/D,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAe;IAC1B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,oBAAoB,CAAkC;IAC9D,OAAO,CAAC,sBAAsB,CAA0B;gBAE5C,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY;IAMrD,QAAQ,IAAI,eAAe;IAI3B,OAAO,IAAI,IAAI;IA8Cf,UAAU,IAAI,IAAI;IAelB,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAavC,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAiBzC;;OAEG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAa7E,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAgErB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,mBAAmB;CAM5B"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
export class ActionCableClient extends EventEmitter {
|
|
4
|
+
config;
|
|
5
|
+
jwt;
|
|
6
|
+
ws = null;
|
|
7
|
+
state = 'disconnected';
|
|
8
|
+
reconnectAttempts = 0;
|
|
9
|
+
reconnectTimer = null;
|
|
10
|
+
pendingSubscriptions = new Map();
|
|
11
|
+
confirmedSubscriptions = new Set();
|
|
12
|
+
constructor(config, jwt) {
|
|
13
|
+
super();
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.jwt = jwt;
|
|
16
|
+
}
|
|
17
|
+
getState() {
|
|
18
|
+
return this.state;
|
|
19
|
+
}
|
|
20
|
+
connect() {
|
|
21
|
+
if (this.state === 'connected' || this.state === 'connecting')
|
|
22
|
+
return;
|
|
23
|
+
this.state = 'connecting';
|
|
24
|
+
this.emit('stateChange', this.state);
|
|
25
|
+
const token = this.jwt.getToken();
|
|
26
|
+
const wsHost = this.config.host.replace(/^http/, 'ws');
|
|
27
|
+
const url = `${wsHost}/cable?token=${encodeURIComponent(token)}&tenant_id=${encodeURIComponent(this.config.tenant_id)}`;
|
|
28
|
+
this.ws = new WebSocket(url);
|
|
29
|
+
this.ws.on('open', () => {
|
|
30
|
+
// Wait for welcome message before considering connected
|
|
31
|
+
});
|
|
32
|
+
this.ws.on('message', (data) => {
|
|
33
|
+
try {
|
|
34
|
+
const raw = data.toString();
|
|
35
|
+
const envelope = JSON.parse(raw);
|
|
36
|
+
if (envelope.type !== 'ping') {
|
|
37
|
+
console.log(`[ActionCable] ← ${raw.substring(0, 200)}`);
|
|
38
|
+
}
|
|
39
|
+
this.handleMessage(envelope);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
console.log(`[ActionCable] ← (unparseable): ${data.toString().substring(0, 100)}`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
this.ws.on('close', (code, reason) => {
|
|
46
|
+
console.log(`[ActionCable] Connection closed: code=${code}, reason=${reason.toString()}`);
|
|
47
|
+
const previousState = this.state;
|
|
48
|
+
this.confirmedSubscriptions.clear();
|
|
49
|
+
this.state = 'disconnected';
|
|
50
|
+
this.emit('stateChange', this.state);
|
|
51
|
+
if (previousState === 'connected' || previousState === 'connecting') {
|
|
52
|
+
this.scheduleReconnect();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
this.ws.on('error', (err) => {
|
|
56
|
+
this.emit('error', err);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
disconnect() {
|
|
60
|
+
this.clearReconnectTimer();
|
|
61
|
+
this.reconnectAttempts = 0;
|
|
62
|
+
if (this.ws) {
|
|
63
|
+
this.ws.removeAllListeners();
|
|
64
|
+
this.ws.close();
|
|
65
|
+
this.ws = null;
|
|
66
|
+
}
|
|
67
|
+
this.confirmedSubscriptions.clear();
|
|
68
|
+
this.state = 'disconnected';
|
|
69
|
+
this.emit('stateChange', this.state);
|
|
70
|
+
}
|
|
71
|
+
subscribe(conversationId) {
|
|
72
|
+
const identifier = JSON.stringify({
|
|
73
|
+
channel: 'ConversationChannel',
|
|
74
|
+
conversation_id: conversationId,
|
|
75
|
+
});
|
|
76
|
+
this.pendingSubscriptions.set(identifier, String(conversationId));
|
|
77
|
+
if (this.state === 'connected' && this.ws) {
|
|
78
|
+
this.sendSubscribe(identifier);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
unsubscribe(conversationId) {
|
|
82
|
+
const identifier = JSON.stringify({
|
|
83
|
+
channel: 'ConversationChannel',
|
|
84
|
+
conversation_id: conversationId,
|
|
85
|
+
});
|
|
86
|
+
this.pendingSubscriptions.delete(identifier);
|
|
87
|
+
this.confirmedSubscriptions.delete(identifier);
|
|
88
|
+
if (this.state === 'connected' && this.ws) {
|
|
89
|
+
this.ws.send(JSON.stringify({
|
|
90
|
+
command: 'unsubscribe',
|
|
91
|
+
identifier,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Subscribe to a generic ActionCable channel (not conversation-specific).
|
|
97
|
+
*/
|
|
98
|
+
subscribeChannel(channelName, params) {
|
|
99
|
+
const identifier = JSON.stringify({
|
|
100
|
+
channel: channelName,
|
|
101
|
+
...params,
|
|
102
|
+
});
|
|
103
|
+
this.pendingSubscriptions.set(identifier, `channel:${channelName}`);
|
|
104
|
+
if (this.state === 'connected' && this.ws) {
|
|
105
|
+
this.sendSubscribe(identifier);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
sendSubscribe(identifier) {
|
|
109
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
110
|
+
this.ws.send(JSON.stringify({
|
|
111
|
+
command: 'subscribe',
|
|
112
|
+
identifier,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
handleMessage(envelope) {
|
|
117
|
+
switch (envelope.type) {
|
|
118
|
+
case 'welcome':
|
|
119
|
+
this.state = 'connected';
|
|
120
|
+
this.reconnectAttempts = 0;
|
|
121
|
+
this.emit('stateChange', this.state);
|
|
122
|
+
// Re-subscribe to all pending subscriptions (skip already confirmed)
|
|
123
|
+
for (const identifier of this.pendingSubscriptions.keys()) {
|
|
124
|
+
if (!this.confirmedSubscriptions.has(identifier)) {
|
|
125
|
+
this.sendSubscribe(identifier);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
case 'ping':
|
|
130
|
+
// ActionCable ping — no response needed
|
|
131
|
+
break;
|
|
132
|
+
case 'confirm_subscription':
|
|
133
|
+
if (envelope.identifier) {
|
|
134
|
+
this.confirmedSubscriptions.add(envelope.identifier);
|
|
135
|
+
this.emit('subscribed', envelope.identifier);
|
|
136
|
+
}
|
|
137
|
+
break;
|
|
138
|
+
case 'reject_subscription':
|
|
139
|
+
if (envelope.identifier) {
|
|
140
|
+
this.emit('rejected', envelope.identifier);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case 'disconnect':
|
|
144
|
+
if (envelope.reconnect === false) {
|
|
145
|
+
this.disconnect();
|
|
146
|
+
}
|
|
147
|
+
break;
|
|
148
|
+
default:
|
|
149
|
+
// Data message — has identifier + message but no type
|
|
150
|
+
if (envelope.identifier && envelope.message) {
|
|
151
|
+
try {
|
|
152
|
+
const parsed = JSON.parse(envelope.identifier);
|
|
153
|
+
if (parsed.channel === 'SilolinkControlChannel') {
|
|
154
|
+
// Control channel message — route to control handler
|
|
155
|
+
this.emit('control', { data: envelope.message });
|
|
156
|
+
}
|
|
157
|
+
else if (parsed.conversation_id) {
|
|
158
|
+
// Conversation message
|
|
159
|
+
this.emit('message', {
|
|
160
|
+
conversationId: parsed.conversation_id,
|
|
161
|
+
data: envelope.message,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
// Ignore malformed identifiers
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
scheduleReconnect() {
|
|
173
|
+
if (this.reconnectAttempts >= this.config.max_reconnect_attempts) {
|
|
174
|
+
this.emit('maxReconnectAttemptsReached');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
this.state = 'reconnecting';
|
|
178
|
+
this.emit('stateChange', this.state);
|
|
179
|
+
// Exponential backoff: 1s, 2s, 4s, 8s, ... capped at 60s
|
|
180
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 60000);
|
|
181
|
+
this.reconnectAttempts++;
|
|
182
|
+
this.reconnectTimer = setTimeout(() => {
|
|
183
|
+
this.ws = null;
|
|
184
|
+
this.connect();
|
|
185
|
+
}, delay);
|
|
186
|
+
}
|
|
187
|
+
clearReconnectTimer() {
|
|
188
|
+
if (this.reconnectTimer) {
|
|
189
|
+
clearTimeout(this.reconnectTimer);
|
|
190
|
+
this.reconnectTimer = null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=action-cable-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-cable-client.js","sourceRoot":"","sources":["../../src/cable/action-cable-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ActionCableClient } from './action-cable-client.js';
|
|
2
|
+
import type { MessageQueue } from '../core/message-queue.js';
|
|
3
|
+
import type { SessionManager } from '../core/session-manager.js';
|
|
4
|
+
import type { ClaudeLauncher } from '../core/claude-launcher.js';
|
|
5
|
+
import type { DSiloedClient } from '../api/dsiloed-client.js';
|
|
6
|
+
export declare class SubscriptionManager {
|
|
7
|
+
private cableClient;
|
|
8
|
+
private messageQueue;
|
|
9
|
+
private sessionManager;
|
|
10
|
+
private claudeLauncher;
|
|
11
|
+
private dsiloedClient;
|
|
12
|
+
private outboundMessageIds;
|
|
13
|
+
private pendingMessages;
|
|
14
|
+
constructor(cableClient: ActionCableClient, messageQueue: MessageQueue, sessionManager: SessionManager, claudeLauncher?: ClaudeLauncher, dsiloedClient?: DSiloedClient);
|
|
15
|
+
subscribe(conversationId: number): void;
|
|
16
|
+
unsubscribe(conversationId: number): void;
|
|
17
|
+
/**
|
|
18
|
+
* Clean up pending messages older than 5 minutes (session never registered).
|
|
19
|
+
*/
|
|
20
|
+
cleanupStalePendingMessages(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Drain any buffered messages for a conversation into the session's queue.
|
|
23
|
+
* Called after a session registers for a conversation.
|
|
24
|
+
*/
|
|
25
|
+
drainPendingMessages(conversationId: number, sessionId: string): void;
|
|
26
|
+
trackOutboundMessageId(messageId: number): void;
|
|
27
|
+
private handleCableMessage;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=subscription-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-manager.d.ts","sourceRoot":"","sources":["../../src/cable/subscription-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAK9D,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,kBAAkB,CAAgB;IAE1C,OAAO,CAAC,eAAe,CAA4C;gBAGjE,WAAW,EAAE,iBAAiB,EAC9B,YAAY,EAAE,YAAY,EAC1B,cAAc,EAAE,cAAc,EAC9B,cAAc,CAAC,EAAE,cAAc,EAC/B,aAAa,CAAC,EAAE,aAAa;IAa/B,SAAS,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAIvC,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAIzC;;OAEG;IACH,2BAA2B,IAAI,IAAI;IAanC;;;OAGG;IACH,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAWrE,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAO/C,OAAO,CAAC,kBAAkB;CA0E3B"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const MAX_TRACKED_IDS = 100;
|
|
2
|
+
export class SubscriptionManager {
|
|
3
|
+
cableClient;
|
|
4
|
+
messageQueue;
|
|
5
|
+
sessionManager;
|
|
6
|
+
claudeLauncher;
|
|
7
|
+
dsiloedClient;
|
|
8
|
+
outboundMessageIds = [];
|
|
9
|
+
// Buffer messages for conversations that don't have a session yet (pending launch)
|
|
10
|
+
pendingMessages = new Map();
|
|
11
|
+
constructor(cableClient, messageQueue, sessionManager, claudeLauncher, dsiloedClient) {
|
|
12
|
+
this.cableClient = cableClient;
|
|
13
|
+
this.messageQueue = messageQueue;
|
|
14
|
+
this.sessionManager = sessionManager;
|
|
15
|
+
this.claudeLauncher = claudeLauncher || null;
|
|
16
|
+
this.dsiloedClient = dsiloedClient || null;
|
|
17
|
+
this.cableClient.on('message', (event) => {
|
|
18
|
+
this.handleCableMessage(event.conversationId, event.data);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
subscribe(conversationId) {
|
|
22
|
+
this.cableClient.subscribe(conversationId);
|
|
23
|
+
}
|
|
24
|
+
unsubscribe(conversationId) {
|
|
25
|
+
this.cableClient.unsubscribe(conversationId);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Clean up pending messages older than 5 minutes (session never registered).
|
|
29
|
+
*/
|
|
30
|
+
cleanupStalePendingMessages() {
|
|
31
|
+
const maxAge = 5 * 60 * 1000;
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
for (const [convId, msgs] of this.pendingMessages) {
|
|
34
|
+
const fresh = msgs.filter(m => now - m.receivedAt.getTime() < maxAge);
|
|
35
|
+
if (fresh.length === 0) {
|
|
36
|
+
this.pendingMessages.delete(convId);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.pendingMessages.set(convId, fresh);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Drain any buffered messages for a conversation into the session's queue.
|
|
45
|
+
* Called after a session registers for a conversation.
|
|
46
|
+
*/
|
|
47
|
+
drainPendingMessages(conversationId, sessionId) {
|
|
48
|
+
const pending = this.pendingMessages.get(conversationId);
|
|
49
|
+
if (pending && pending.length > 0) {
|
|
50
|
+
console.log(`[SiloLink] Draining ${pending.length} buffered message(s) for conversation ${conversationId}`);
|
|
51
|
+
for (const msg of pending) {
|
|
52
|
+
this.messageQueue.enqueue(sessionId, msg);
|
|
53
|
+
}
|
|
54
|
+
this.pendingMessages.delete(conversationId);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
trackOutboundMessageId(messageId) {
|
|
58
|
+
this.outboundMessageIds.push(messageId);
|
|
59
|
+
if (this.outboundMessageIds.length > MAX_TRACKED_IDS) {
|
|
60
|
+
this.outboundMessageIds.shift();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
handleCableMessage(conversationId, data) {
|
|
64
|
+
const msg = data;
|
|
65
|
+
// Silently skip non-message events (typing indicators, read receipts, etc.)
|
|
66
|
+
if (msg.type !== 'new_message' || !msg.message)
|
|
67
|
+
return;
|
|
68
|
+
console.log(`[SiloLink] Cable message received: conv=${conversationId}, msgId=${msg.message?.id}, sender=${msg.message?.sender_name}`);
|
|
69
|
+
// Echo prevention — skip our own messages
|
|
70
|
+
// Primary: check tracked outbound message IDs
|
|
71
|
+
if (this.outboundMessageIds.includes(msg.message.id)) {
|
|
72
|
+
console.log(`[SiloLink] Skipping echo (tracked ID): ${msg.message.id}`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Extract content — handle both nested object and string formats
|
|
76
|
+
const rawMessage = msg.message.message;
|
|
77
|
+
const content = typeof rawMessage === 'string'
|
|
78
|
+
? rawMessage
|
|
79
|
+
: rawMessage?.content || '';
|
|
80
|
+
const role = typeof rawMessage === 'string'
|
|
81
|
+
? 'user'
|
|
82
|
+
: rawMessage?.role || 'user';
|
|
83
|
+
// Secondary echo prevention: skip messages with our prefix
|
|
84
|
+
if (content.startsWith('**[Claude Code]**')) {
|
|
85
|
+
console.log(`[SiloLink] Skipping echo (prefix match): ${content.substring(0, 50)}`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(`[SiloLink] Inbound message accepted: id=${msg.message.id}, role=${role}, content="${content.substring(0, 80)}"`);
|
|
89
|
+
const inbound = {
|
|
90
|
+
id: msg.message.id,
|
|
91
|
+
role,
|
|
92
|
+
content,
|
|
93
|
+
senderName: msg.message.sender_name,
|
|
94
|
+
receivedAt: new Date(),
|
|
95
|
+
};
|
|
96
|
+
// Route to correct session by conversation ID
|
|
97
|
+
const session = this.sessionManager.getByConversationId(conversationId);
|
|
98
|
+
if (session) {
|
|
99
|
+
console.log(`[SiloLink] Enqueuing to session ${session.sessionId}`);
|
|
100
|
+
this.messageQueue.enqueue(session.sessionId, inbound);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log(`[SiloLink] No session found for conversation ${conversationId}`);
|
|
104
|
+
// Auto-launch Claude Code if configured and no session exists
|
|
105
|
+
if (this.claudeLauncher?.isEnabled()) {
|
|
106
|
+
console.log(`[SiloLink] Triggering auto-launch for inbound message...`);
|
|
107
|
+
// Let the user know we're restarting
|
|
108
|
+
if (this.dsiloedClient) {
|
|
109
|
+
this.dsiloedClient.postMessage(conversationId, '**[Claude Code]** Restarting session... Claude is initializing and will be ready shortly.')
|
|
110
|
+
.catch(() => { });
|
|
111
|
+
}
|
|
112
|
+
// Buffer the message so it's delivered once the session registers
|
|
113
|
+
if (!this.pendingMessages.has(conversationId)) {
|
|
114
|
+
this.pendingMessages.set(conversationId, []);
|
|
115
|
+
}
|
|
116
|
+
this.pendingMessages.get(conversationId).push(inbound);
|
|
117
|
+
console.log(`[SiloLink] Buffered message for conversation ${conversationId} (pending session)`);
|
|
118
|
+
this.claudeLauncher.launchOnMessage(conversationId).catch(err => {
|
|
119
|
+
console.error(`[SiloLink] Auto-launch failed: ${err.message}`);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=subscription-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-manager.js","sourceRoot":"","sources":["../../src/cable/subscription-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AA8J9B,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { interactiveConfig, configExists, loadConfig } from '../config/config-manager.js';
|
|
6
|
+
import { writePidFile, readPidFile, removePidFile, isProcessRunning } from './daemon.js';
|
|
7
|
+
import { Bridge } from '../core/bridge.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('silolink')
|
|
11
|
+
.description('Claude Code Remote Bridge — connects Claude Code to DSiloed')
|
|
12
|
+
.version('0.1.0');
|
|
13
|
+
program
|
|
14
|
+
.command('start')
|
|
15
|
+
.description('Start the SiloLink bridge')
|
|
16
|
+
.option('-d, --daemon', 'Run in background')
|
|
17
|
+
.action(async (opts) => {
|
|
18
|
+
if (!configExists()) {
|
|
19
|
+
console.error('Config not found. Run "silolink config" first.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
// Check if already running
|
|
23
|
+
const existingPid = readPidFile();
|
|
24
|
+
if (existingPid && isProcessRunning(existingPid)) {
|
|
25
|
+
console.log(`SiloLink already running (PID ${existingPid})`);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
if (opts.daemon) {
|
|
29
|
+
// Spawn detached process
|
|
30
|
+
const entryPoint = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../index.js');
|
|
31
|
+
const child = spawn(process.execPath, [entryPoint, 'start'], {
|
|
32
|
+
detached: true,
|
|
33
|
+
stdio: 'ignore',
|
|
34
|
+
});
|
|
35
|
+
child.unref();
|
|
36
|
+
console.log(`SiloLink started in background (PID ${child.pid})`);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
// Foreground mode
|
|
40
|
+
writePidFile();
|
|
41
|
+
process.on('exit', removePidFile);
|
|
42
|
+
const bridge = new Bridge();
|
|
43
|
+
await bridge.start();
|
|
44
|
+
});
|
|
45
|
+
program
|
|
46
|
+
.command('stop')
|
|
47
|
+
.description('Stop the SiloLink bridge')
|
|
48
|
+
.action(() => {
|
|
49
|
+
const pid = readPidFile();
|
|
50
|
+
if (!pid) {
|
|
51
|
+
console.log('SiloLink is not running (no PID file)');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!isProcessRunning(pid)) {
|
|
55
|
+
console.log('SiloLink is not running (stale PID file)');
|
|
56
|
+
removePidFile();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
process.kill(pid, 'SIGTERM');
|
|
61
|
+
console.log(`Sent SIGTERM to SiloLink (PID ${pid})`);
|
|
62
|
+
removePidFile();
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error(`Failed to stop SiloLink: ${err}`);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
program
|
|
69
|
+
.command('status')
|
|
70
|
+
.description('Show SiloLink status')
|
|
71
|
+
.action(async () => {
|
|
72
|
+
const pid = readPidFile();
|
|
73
|
+
if (!pid || !isProcessRunning(pid)) {
|
|
74
|
+
console.log('SiloLink is not running');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log(`SiloLink running (PID ${pid})`);
|
|
78
|
+
try {
|
|
79
|
+
const config = loadConfig();
|
|
80
|
+
const res = await fetch(`http://localhost:${config.mcp_port}/health`);
|
|
81
|
+
const status = await res.json();
|
|
82
|
+
console.log(` ActionCable: ${status.cable}`);
|
|
83
|
+
console.log(` Sessions: ${status.sessions}`);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
console.log(' (could not reach health endpoint)');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
program
|
|
90
|
+
.command('sessions')
|
|
91
|
+
.description('List active sessions')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
try {
|
|
94
|
+
const config = loadConfig();
|
|
95
|
+
const res = await fetch(`http://localhost:${config.mcp_port}/health`);
|
|
96
|
+
const status = (await res.json());
|
|
97
|
+
console.log(`Active sessions: ${status.sessions}`);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
console.log('SiloLink is not running or unreachable');
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
program
|
|
104
|
+
.command('launch')
|
|
105
|
+
.description('Launch a Claude Code session connected to SiloLink')
|
|
106
|
+
.option('-p, --prompt <prompt>', 'Custom prompt for Claude Code')
|
|
107
|
+
.action(async (opts) => {
|
|
108
|
+
if (!configExists()) {
|
|
109
|
+
console.error('Config not found. Run "silolink config" first.');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
const config = loadConfig();
|
|
113
|
+
if (!config.claude_command || !config.claude_working_directory) {
|
|
114
|
+
console.error('Claude launcher not configured. Set claude_command and claude_working_directory in ~/.silolink/config.json');
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
// Import dynamically to avoid circular deps
|
|
118
|
+
const { ClaudeLauncher } = await import('../core/claude-launcher.js');
|
|
119
|
+
const { SessionManager } = await import('../core/session-manager.js');
|
|
120
|
+
const sessionManager = new SessionManager();
|
|
121
|
+
const launcher = new ClaudeLauncher(config, sessionManager);
|
|
122
|
+
if (opts.prompt) {
|
|
123
|
+
config.claude_session_prompt = opts.prompt;
|
|
124
|
+
}
|
|
125
|
+
const launched = await launcher.launch('cli');
|
|
126
|
+
if (!launched) {
|
|
127
|
+
console.error('Failed to launch Claude Code');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
console.log('Claude Code launched. It will register with SiloLink automatically.');
|
|
131
|
+
// Keep process alive until Claude exits
|
|
132
|
+
process.on('SIGINT', () => {
|
|
133
|
+
launcher.kill();
|
|
134
|
+
process.exit(0);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
program
|
|
138
|
+
.command('config')
|
|
139
|
+
.description('Configure SiloLink')
|
|
140
|
+
.action(async () => {
|
|
141
|
+
await interactiveConfig();
|
|
142
|
+
});
|
|
143
|
+
export { program };
|
|
144
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/cli/daemon.ts"],"names":[],"mappings":"AAMA,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAO3C;AAED,wBAAgB,aAAa,IAAI,IAAI,CAMpC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOrD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getConfigDir } from '../config/config-manager.js';
|
|
4
|
+
const PID_FILE = path.join(getConfigDir(), 'silolink.pid');
|
|
5
|
+
export function writePidFile() {
|
|
6
|
+
fs.mkdirSync(path.dirname(PID_FILE), { recursive: true });
|
|
7
|
+
fs.writeFileSync(PID_FILE, String(process.pid));
|
|
8
|
+
}
|
|
9
|
+
export function readPidFile() {
|
|
10
|
+
try {
|
|
11
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim(), 10);
|
|
12
|
+
return isNaN(pid) ? null : pid;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function removePidFile() {
|
|
19
|
+
try {
|
|
20
|
+
fs.unlinkSync(PID_FILE);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Ignore if already removed
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function isProcessRunning(pid) {
|
|
27
|
+
try {
|
|
28
|
+
process.kill(pid, 0);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../src/cli/daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,cAAc,CAAC,CAAC;AAE3D,MAAM,UAAU,YAAY;IAC1B,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACpE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SiloLinkConfig } from '../types/index.js';
|
|
2
|
+
export declare function getConfigDir(): string;
|
|
3
|
+
export declare function loadConfig(): SiloLinkConfig;
|
|
4
|
+
export declare function saveConfig(config: SiloLinkConfig): void;
|
|
5
|
+
export declare function configExists(): boolean;
|
|
6
|
+
export declare function interactiveConfig(): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=config-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.d.ts","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAoBxD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,UAAU,IAAI,cAAc,CAS3C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAGvD;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA0CvD"}
|