@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,76 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import readline from 'node:readline';
|
|
5
|
+
const CONFIG_DIR = path.join(os.homedir(), '.silolink');
|
|
6
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
host: 'https://www.dsiloed.com',
|
|
9
|
+
tenant_id: 'harmoniq',
|
|
10
|
+
shared_key: '',
|
|
11
|
+
user_sub: '',
|
|
12
|
+
mcp_port: 3579,
|
|
13
|
+
reconnect_interval_ms: 5000,
|
|
14
|
+
max_reconnect_attempts: 20,
|
|
15
|
+
claude_command: 'claude',
|
|
16
|
+
claude_working_directory: '',
|
|
17
|
+
claude_auto_respawn: false,
|
|
18
|
+
claude_idle_timeout_ms: 30000,
|
|
19
|
+
claude_session_prompt: 'Register with SiloLink as "portablemind" and enter the poll loop.',
|
|
20
|
+
};
|
|
21
|
+
export function getConfigDir() {
|
|
22
|
+
return CONFIG_DIR;
|
|
23
|
+
}
|
|
24
|
+
export function loadConfig() {
|
|
25
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
26
|
+
throw new Error(`Config not found at ${CONFIG_PATH}. Run "silolink config" to set up.`);
|
|
27
|
+
}
|
|
28
|
+
const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
return { ...DEFAULT_CONFIG, ...parsed };
|
|
31
|
+
}
|
|
32
|
+
export function saveConfig(config) {
|
|
33
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
34
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n');
|
|
35
|
+
}
|
|
36
|
+
export function configExists() {
|
|
37
|
+
return fs.existsSync(CONFIG_PATH);
|
|
38
|
+
}
|
|
39
|
+
export async function interactiveConfig() {
|
|
40
|
+
const rl = readline.createInterface({
|
|
41
|
+
input: process.stdin,
|
|
42
|
+
output: process.stdout,
|
|
43
|
+
});
|
|
44
|
+
const ask = (question, defaultValue) => new Promise((resolve) => {
|
|
45
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : '';
|
|
46
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
47
|
+
resolve(answer.trim() || defaultValue || '');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
let existing = DEFAULT_CONFIG;
|
|
51
|
+
try {
|
|
52
|
+
existing = loadConfig();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// use defaults
|
|
56
|
+
}
|
|
57
|
+
console.log('\nSiloLink Configuration\n');
|
|
58
|
+
const host = await ask('DSiloed host', existing.host);
|
|
59
|
+
const tenant_id = await ask('Tenant ID', existing.tenant_id);
|
|
60
|
+
const shared_key = await ask('Shared key', existing.shared_key || undefined);
|
|
61
|
+
const user_sub = await ask('User login (email or username)', existing.user_sub || undefined);
|
|
62
|
+
const mcp_port_str = await ask('MCP port', String(existing.mcp_port));
|
|
63
|
+
const mcp_port = parseInt(mcp_port_str, 10) || existing.mcp_port;
|
|
64
|
+
const config = {
|
|
65
|
+
...existing,
|
|
66
|
+
host,
|
|
67
|
+
tenant_id,
|
|
68
|
+
shared_key,
|
|
69
|
+
user_sub,
|
|
70
|
+
mcp_port,
|
|
71
|
+
};
|
|
72
|
+
saveConfig(config);
|
|
73
|
+
console.log(`\nConfig saved to ${CONFIG_PATH}\n`);
|
|
74
|
+
rl.close();
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=config-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-manager.js","sourceRoot":"","sources":["../../src/config/config-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SiloLinkConfig } from '../types/index.js';
|
|
2
|
+
export declare class JwtGenerator {
|
|
3
|
+
private config;
|
|
4
|
+
private currentToken;
|
|
5
|
+
private tokenExpiry;
|
|
6
|
+
private refreshTimer;
|
|
7
|
+
private onRefresh;
|
|
8
|
+
constructor(config: SiloLinkConfig);
|
|
9
|
+
generate(): string;
|
|
10
|
+
getToken(): string;
|
|
11
|
+
private isExpiringSoon;
|
|
12
|
+
startAutoRefresh(onRefresh: (token: string) => void): void;
|
|
13
|
+
stopAutoRefresh(): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=jwt-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-generator.d.ts","sourceRoot":"","sources":["../../src/config/jwt-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKxD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,SAAS,CAA0C;gBAE/C,MAAM,EAAE,cAAc;IAIlC,QAAQ,IAAI,MAAM;IAiBlB,QAAQ,IAAI,MAAM;IAOlB,OAAO,CAAC,cAAc;IAMtB,gBAAgB,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAU1D,eAAe,IAAI,IAAI;CAMxB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
const TOKEN_LIFETIME_HOURS = 24;
|
|
3
|
+
const REFRESH_INTERVAL_HOURS = 12;
|
|
4
|
+
export class JwtGenerator {
|
|
5
|
+
config;
|
|
6
|
+
currentToken = null;
|
|
7
|
+
tokenExpiry = 0;
|
|
8
|
+
refreshTimer = null;
|
|
9
|
+
onRefresh = null;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
generate() {
|
|
14
|
+
const now = Math.floor(Date.now() / 1000);
|
|
15
|
+
const exp = now + TOKEN_LIFETIME_HOURS * 3600;
|
|
16
|
+
const payload = {
|
|
17
|
+
sub: this.config.user_sub,
|
|
18
|
+
iat: now,
|
|
19
|
+
exp,
|
|
20
|
+
};
|
|
21
|
+
this.currentToken = jwt.sign(payload, this.config.shared_key, {
|
|
22
|
+
algorithm: 'HS256',
|
|
23
|
+
});
|
|
24
|
+
this.tokenExpiry = exp;
|
|
25
|
+
return this.currentToken;
|
|
26
|
+
}
|
|
27
|
+
getToken() {
|
|
28
|
+
if (!this.currentToken || this.isExpiringSoon()) {
|
|
29
|
+
return this.generate();
|
|
30
|
+
}
|
|
31
|
+
return this.currentToken;
|
|
32
|
+
}
|
|
33
|
+
isExpiringSoon() {
|
|
34
|
+
const now = Math.floor(Date.now() / 1000);
|
|
35
|
+
// Refresh if less than 1 hour remaining
|
|
36
|
+
return this.tokenExpiry - now < 3600;
|
|
37
|
+
}
|
|
38
|
+
startAutoRefresh(onRefresh) {
|
|
39
|
+
this.onRefresh = onRefresh;
|
|
40
|
+
this.refreshTimer = setInterval(() => {
|
|
41
|
+
const token = this.generate();
|
|
42
|
+
if (this.onRefresh) {
|
|
43
|
+
this.onRefresh(token);
|
|
44
|
+
}
|
|
45
|
+
}, REFRESH_INTERVAL_HOURS * 3600 * 1000);
|
|
46
|
+
}
|
|
47
|
+
stopAutoRefresh() {
|
|
48
|
+
if (this.refreshTimer) {
|
|
49
|
+
clearInterval(this.refreshTimer);
|
|
50
|
+
this.refreshTimer = null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=jwt-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-generator.js","sourceRoot":"","sources":["../../src/config/jwt-generator.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAG/B,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAChC,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAElC,MAAM,OAAO,YAAY;IACf,MAAM,CAAiB;IACvB,YAAY,GAAkB,IAAI,CAAC;IACnC,WAAW,GAAW,CAAC,CAAC;IACxB,YAAY,GAA0C,IAAI,CAAC;IAC3D,SAAS,GAAqC,IAAI,CAAC;IAE3D,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,QAAQ;QACN,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,GAAG,GAAG,oBAAoB,GAAG,IAAI,CAAC;QAE9C,MAAM,OAAO,GAAG;YACd,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YACzB,GAAG,EAAE,GAAG;YACR,GAAG;SACJ,CAAC;QAEF,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE;YAC5D,SAAS,EAAE,OAAO;SACnB,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAEO,cAAc;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC1C,wCAAwC;QACxC,OAAO,IAAI,CAAC,WAAW,GAAG,GAAG,GAAG,IAAI,CAAC;IACvC,CAAC;IAED,gBAAgB,CAAC,SAAkC;QACjD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;YACnC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC;QACH,CAAC,EAAE,sBAAsB,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,eAAe;QACb,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ConnectionState } from '../types/index.js';
|
|
2
|
+
export declare class Bridge {
|
|
3
|
+
private config;
|
|
4
|
+
private jwt;
|
|
5
|
+
private dsiloedClient;
|
|
6
|
+
private cableClient;
|
|
7
|
+
private subscriptionManager;
|
|
8
|
+
private sessionManager;
|
|
9
|
+
private messageQueue;
|
|
10
|
+
private claudeLauncher;
|
|
11
|
+
private mcpServer;
|
|
12
|
+
private shuttingDown;
|
|
13
|
+
constructor();
|
|
14
|
+
start(): Promise<void>;
|
|
15
|
+
stop(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Clean up stale agent sessions from previous SiloLink runs.
|
|
18
|
+
* Also pre-subscribe to their conversations so we can receive messages
|
|
19
|
+
* even before a Claude session registers (enables auto-launch).
|
|
20
|
+
*/
|
|
21
|
+
private cleanupStaleAgentSessions;
|
|
22
|
+
/**
|
|
23
|
+
* Handle commands received from the SiloLink control channel.
|
|
24
|
+
* These are broadcast by the PortableMind UI via the API.
|
|
25
|
+
*/
|
|
26
|
+
private handleControlCommand;
|
|
27
|
+
getStatus(): {
|
|
28
|
+
state: ConnectionState;
|
|
29
|
+
sessions: number;
|
|
30
|
+
config: {
|
|
31
|
+
host: string;
|
|
32
|
+
tenant: string;
|
|
33
|
+
port: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/core/bridge.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzE,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAe;IAC1B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,YAAY,CAAS;;IAkCvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyDtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyC3B;;;;OAIG;YACW,yBAAyB;IAiDvC;;;OAGG;YACW,oBAAoB;IAsElC,SAAS,IAAI;QAAE,KAAK,EAAE,eAAe,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE;CAWlH"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { loadConfig } from '../config/config-manager.js';
|
|
2
|
+
import { JwtGenerator } from '../config/jwt-generator.js';
|
|
3
|
+
import { DSiloedClient } from '../api/dsiloed-client.js';
|
|
4
|
+
import { ActionCableClient } from '../cable/action-cable-client.js';
|
|
5
|
+
import { SubscriptionManager } from '../cable/subscription-manager.js';
|
|
6
|
+
import { SessionManager } from '../core/session-manager.js';
|
|
7
|
+
import { MessageQueue } from '../core/message-queue.js';
|
|
8
|
+
import { ClaudeLauncher } from '../core/claude-launcher.js';
|
|
9
|
+
import { SiloLinkMcpServer } from '../mcp/server.js';
|
|
10
|
+
export class Bridge {
|
|
11
|
+
config;
|
|
12
|
+
jwt;
|
|
13
|
+
dsiloedClient;
|
|
14
|
+
cableClient;
|
|
15
|
+
subscriptionManager;
|
|
16
|
+
sessionManager;
|
|
17
|
+
messageQueue;
|
|
18
|
+
claudeLauncher;
|
|
19
|
+
mcpServer;
|
|
20
|
+
shuttingDown = false;
|
|
21
|
+
constructor() {
|
|
22
|
+
this.config = loadConfig();
|
|
23
|
+
this.jwt = new JwtGenerator(this.config);
|
|
24
|
+
this.dsiloedClient = new DSiloedClient(this.config, this.jwt);
|
|
25
|
+
this.cableClient = new ActionCableClient(this.config, this.jwt);
|
|
26
|
+
this.sessionManager = new SessionManager((session) => {
|
|
27
|
+
// When idle sessions are cleaned up, complete their agent sessions
|
|
28
|
+
if (session.agentSessionId) {
|
|
29
|
+
this.dsiloedClient.completeAgentSession(session.agentSessionId)
|
|
30
|
+
.catch(err => console.error(`[SiloLink] Failed to complete idle agent session ${session.agentSessionId}:`, err));
|
|
31
|
+
}
|
|
32
|
+
console.log(`[SiloLink] Idle session cleaned up: ${session.sessionId} (conv: ${session.conversationId})`);
|
|
33
|
+
});
|
|
34
|
+
this.messageQueue = new MessageQueue();
|
|
35
|
+
this.claudeLauncher = new ClaudeLauncher(this.config, this.sessionManager, this.dsiloedClient, this.messageQueue);
|
|
36
|
+
this.subscriptionManager = new SubscriptionManager(this.cableClient, this.messageQueue, this.sessionManager, this.claudeLauncher, this.dsiloedClient);
|
|
37
|
+
this.mcpServer = new SiloLinkMcpServer({
|
|
38
|
+
config: this.config,
|
|
39
|
+
sessionManager: this.sessionManager,
|
|
40
|
+
messageQueue: this.messageQueue,
|
|
41
|
+
dsiloedClient: this.dsiloedClient,
|
|
42
|
+
subscriptionManager: this.subscriptionManager,
|
|
43
|
+
cableClient: this.cableClient,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async start() {
|
|
47
|
+
console.log('SiloLink starting...');
|
|
48
|
+
console.log(` Host: ${this.config.host}`);
|
|
49
|
+
console.log(` Tenant: ${this.config.tenant_id}`);
|
|
50
|
+
console.log(` User: ${this.config.user_sub}`);
|
|
51
|
+
console.log(` MCP Port: ${this.config.mcp_port}`);
|
|
52
|
+
// Generate initial JWT
|
|
53
|
+
this.jwt.generate();
|
|
54
|
+
console.log(' JWT generated');
|
|
55
|
+
// Set up ActionCable event logging
|
|
56
|
+
this.cableClient.on('stateChange', (state) => {
|
|
57
|
+
console.log(` ActionCable: ${state}`);
|
|
58
|
+
});
|
|
59
|
+
this.cableClient.on('error', (err) => {
|
|
60
|
+
console.error(` ActionCable error: ${err.message}`);
|
|
61
|
+
});
|
|
62
|
+
this.cableClient.on('maxReconnectAttemptsReached', () => {
|
|
63
|
+
console.error(' ActionCable: max reconnect attempts reached');
|
|
64
|
+
});
|
|
65
|
+
// Connect ActionCable
|
|
66
|
+
this.cableClient.connect();
|
|
67
|
+
// Subscribe to the SiloLink control channel for launch/stop commands
|
|
68
|
+
this.cableClient.subscribeChannel('SilolinkControlChannel');
|
|
69
|
+
this.cableClient.on('control', (event) => {
|
|
70
|
+
this.handleControlCommand(event.data);
|
|
71
|
+
});
|
|
72
|
+
// Start MCP server
|
|
73
|
+
await this.mcpServer.start(this.config.mcp_port);
|
|
74
|
+
// Start JWT auto-refresh
|
|
75
|
+
this.jwt.startAutoRefresh((token) => {
|
|
76
|
+
console.log(' JWT refreshed, reconnecting ActionCable...');
|
|
77
|
+
this.cableClient.disconnect();
|
|
78
|
+
this.cableClient.connect();
|
|
79
|
+
});
|
|
80
|
+
// Clean up stale agent sessions from previous runs
|
|
81
|
+
await this.cleanupStaleAgentSessions();
|
|
82
|
+
// Start Claude auto-respawn monitor if configured
|
|
83
|
+
this.claudeLauncher.startIdleMonitor();
|
|
84
|
+
// Handle shutdown signals
|
|
85
|
+
const shutdown = () => this.stop();
|
|
86
|
+
process.on('SIGTERM', shutdown);
|
|
87
|
+
process.on('SIGINT', shutdown);
|
|
88
|
+
console.log('SiloLink ready.\n');
|
|
89
|
+
}
|
|
90
|
+
async stop() {
|
|
91
|
+
if (this.shuttingDown)
|
|
92
|
+
return;
|
|
93
|
+
this.shuttingDown = true;
|
|
94
|
+
console.log('\nSiloLink shutting down...');
|
|
95
|
+
// Stop JWT refresh
|
|
96
|
+
this.jwt.stopAutoRefresh();
|
|
97
|
+
// Kill Claude processes FIRST so they don't lose in-flight messages
|
|
98
|
+
this.claudeLauncher.destroy();
|
|
99
|
+
// Then unregister all sessions and complete their agent sessions
|
|
100
|
+
const sessions = this.sessionManager.getAll();
|
|
101
|
+
for (const session of sessions) {
|
|
102
|
+
if (session.agentSessionId) {
|
|
103
|
+
try {
|
|
104
|
+
await this.dsiloedClient.completeAgentSession(session.agentSessionId);
|
|
105
|
+
console.log(` Completed agent session ${session.agentSessionId}`);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error(` Failed to complete agent session ${session.agentSessionId}:`, err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.subscriptionManager.unsubscribe(session.conversationId);
|
|
112
|
+
this.messageQueue.clearSession(session.sessionId);
|
|
113
|
+
this.sessionManager.unregister(session.sessionId);
|
|
114
|
+
}
|
|
115
|
+
// Stop MCP server
|
|
116
|
+
await this.mcpServer.stop();
|
|
117
|
+
// Disconnect ActionCable
|
|
118
|
+
this.cableClient.disconnect();
|
|
119
|
+
// Clean up session manager
|
|
120
|
+
this.sessionManager.destroy();
|
|
121
|
+
console.log('SiloLink stopped.');
|
|
122
|
+
process.exit(0);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Clean up stale agent sessions from previous SiloLink runs.
|
|
126
|
+
* Also pre-subscribe to their conversations so we can receive messages
|
|
127
|
+
* even before a Claude session registers (enables auto-launch).
|
|
128
|
+
*/
|
|
129
|
+
async cleanupStaleAgentSessions() {
|
|
130
|
+
const conversationIds = new Set();
|
|
131
|
+
try {
|
|
132
|
+
const response = await this.dsiloedClient.getActiveAgentSessions('silolink');
|
|
133
|
+
if (response && response.length > 0) {
|
|
134
|
+
console.log(` Cleaning up ${response.length} stale agent session(s)...`);
|
|
135
|
+
for (const session of response) {
|
|
136
|
+
// Capture conversation IDs before completing
|
|
137
|
+
if (session.llm_conversation_id) {
|
|
138
|
+
conversationIds.add(session.llm_conversation_id);
|
|
139
|
+
}
|
|
140
|
+
// Capture resume ID from metadata if available
|
|
141
|
+
if (session.metadata?.claude_resume_id) {
|
|
142
|
+
this.claudeLauncher.setLastResumeId(session.metadata.claude_resume_id);
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
await this.dsiloedClient.completeAgentSession(session.id);
|
|
146
|
+
console.log(` Completed stale session ${session.id} (${session.session_name || 'unnamed'})`);
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
console.error(` Failed to complete stale session ${session.id}:`, err);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
console.warn(' Could not clean up stale sessions (non-fatal):', err);
|
|
156
|
+
}
|
|
157
|
+
// Subscribe to ALL SiloLink conversations so inbound messages trigger auto-launch
|
|
158
|
+
try {
|
|
159
|
+
const allConversations = await this.dsiloedClient.getSiloLinkConversations();
|
|
160
|
+
if (allConversations.length > 0) {
|
|
161
|
+
console.log(` Subscribing to ${allConversations.length} SiloLink conversation(s)...`);
|
|
162
|
+
for (const conv of allConversations) {
|
|
163
|
+
this.subscriptionManager.subscribe(conv.id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
console.warn(' Could not load SiloLink conversations (non-fatal):', err);
|
|
169
|
+
// Fall back to conversations from stale sessions
|
|
170
|
+
if (conversationIds.size > 0) {
|
|
171
|
+
console.log(` Falling back to ${conversationIds.size} conversation(s) from stale sessions...`);
|
|
172
|
+
for (const convId of conversationIds) {
|
|
173
|
+
this.subscriptionManager.subscribe(convId);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Handle commands received from the SiloLink control channel.
|
|
180
|
+
* These are broadcast by the PortableMind UI via the API.
|
|
181
|
+
*/
|
|
182
|
+
async handleControlCommand(data) {
|
|
183
|
+
const cmd = data;
|
|
184
|
+
// Skip messages that originated from SiloLink itself (echoed back)
|
|
185
|
+
if (cmd.source === 'silolink')
|
|
186
|
+
return;
|
|
187
|
+
console.log(`[SiloLink] Control command received: ${cmd.type} (from: ${cmd.requested_by || 'unknown'})`);
|
|
188
|
+
switch (cmd.type) {
|
|
189
|
+
case 'launch': {
|
|
190
|
+
const name = cmd.name || 'portablemind';
|
|
191
|
+
const convId = cmd.conversation_id;
|
|
192
|
+
// Conversation is created by the API — just subscribe and launch
|
|
193
|
+
if (convId) {
|
|
194
|
+
this.subscriptionManager.subscribe(convId);
|
|
195
|
+
}
|
|
196
|
+
// Build prompt
|
|
197
|
+
const registerParams = convId
|
|
198
|
+
? `{ "conversation_id": ${convId} }`
|
|
199
|
+
: `{ "session_name": "${name}" }`;
|
|
200
|
+
const readyInstruction = 'After registering, IMMEDIATELY send a remote_notify message telling the user you are ready (e.g. "Session ready! How can I help?"). Then enter the poll loop.';
|
|
201
|
+
const progressInstruction = 'CRITICAL: The user is NOT watching the terminal — they are communicating via PortableMind. You MUST send a remote_notify progress update after EVERY major step (e.g. file edits, commits, merges, deploys, errors). Do not go silent while working.';
|
|
202
|
+
const sessionPrompt = cmd.prompt
|
|
203
|
+
? `${cmd.prompt}\n\nIMPORTANT: First call mcp__silolink__remote_register with these exact parameters: ${registerParams}. ${readyInstruction} ${progressInstruction} See CLAUDE.md for the poll loop pattern.`
|
|
204
|
+
: `Call mcp__silolink__remote_register with these exact parameters: ${registerParams}. ${readyInstruction} See CLAUDE.md for the poll loop pattern.`;
|
|
205
|
+
this.config.claude_session_prompt = sessionPrompt;
|
|
206
|
+
// Post an initial message so the user knows the session is starting
|
|
207
|
+
if (convId) {
|
|
208
|
+
this.dsiloedClient.postMessage(convId, '**[Claude Code]** Starting session... Claude is initializing and will be ready shortly.')
|
|
209
|
+
.catch(err => console.error('[SiloLink] Failed to post startup message:', err));
|
|
210
|
+
}
|
|
211
|
+
const launched = await this.claudeLauncher.launch('control-channel', convId);
|
|
212
|
+
console.log(`[SiloLink] Launch command result: ${launched ? 'started' : 'skipped (already running?)'}`);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 'stop': {
|
|
216
|
+
if (cmd.conversation_id) {
|
|
217
|
+
this.claudeLauncher.killByConversation(cmd.conversation_id);
|
|
218
|
+
console.log(`[SiloLink] Stop command executed — killed session for conversation ${cmd.conversation_id}`);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
this.claudeLauncher.kill();
|
|
222
|
+
console.log('[SiloLink] Stop command executed — all Claude sessions killed');
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case 'status_request': {
|
|
227
|
+
const status = this.getStatus();
|
|
228
|
+
console.log(`[SiloLink] Status: ${JSON.stringify(status)}`);
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
default:
|
|
232
|
+
console.log(`[SiloLink] Unknown control command: ${cmd.type}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
getStatus() {
|
|
236
|
+
return {
|
|
237
|
+
state: this.cableClient.getState(),
|
|
238
|
+
sessions: this.sessionManager.getAll().length,
|
|
239
|
+
config: {
|
|
240
|
+
host: this.config.host,
|
|
241
|
+
tenant: this.config.tenant_id,
|
|
242
|
+
port: this.config.mcp_port,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/core/bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { SiloLinkConfig } from '../types/index.js';
|
|
2
|
+
import type { SessionManager } from './session-manager.js';
|
|
3
|
+
import type { MessageQueue } from './message-queue.js';
|
|
4
|
+
import type { DSiloedClient } from '../api/dsiloed-client.js';
|
|
5
|
+
/**
|
|
6
|
+
* Manages Claude Code child processes.
|
|
7
|
+
*
|
|
8
|
+
* Strategy:
|
|
9
|
+
* - For SiloLink-spawned sessions: nudge via stdin when idle (preserves context)
|
|
10
|
+
* - Respawn ONLY when the process actually exits
|
|
11
|
+
* - On respawn, use `claude --resume <id>` to restore context
|
|
12
|
+
* - Store Claude session ID in DSiloed agent_session metadata
|
|
13
|
+
* - Kill all child processes on SiloLink shutdown
|
|
14
|
+
*/
|
|
15
|
+
export declare class ClaudeLauncher {
|
|
16
|
+
private config;
|
|
17
|
+
private sessionManager;
|
|
18
|
+
private dsiloedClient;
|
|
19
|
+
private childProcess;
|
|
20
|
+
private tmuxSessions;
|
|
21
|
+
private tmuxMonitorTimer;
|
|
22
|
+
private idleCheckTimer;
|
|
23
|
+
private launchingConversations;
|
|
24
|
+
private messageQueue;
|
|
25
|
+
private enabled;
|
|
26
|
+
private lastNudgeAt;
|
|
27
|
+
private nudgeCount;
|
|
28
|
+
private lastActivitySnapshot;
|
|
29
|
+
private lastClaudeSessionId;
|
|
30
|
+
constructor(config: SiloLinkConfig, sessionManager: SessionManager, dsiloedClient?: DSiloedClient, messageQueue?: MessageQueue);
|
|
31
|
+
isEnabled(): boolean;
|
|
32
|
+
isAutoRespawnEnabled(): boolean;
|
|
33
|
+
hasLiveProcess(): boolean;
|
|
34
|
+
getLastClaudeSessionId(): string | null;
|
|
35
|
+
setLastResumeId(resumeId: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Start the idle monitor that nudges idle sessions or respawns dead ones.
|
|
38
|
+
*/
|
|
39
|
+
startIdleMonitor(): void;
|
|
40
|
+
stopIdleMonitor(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Launch a new Claude Code session. Uses --resume if a previous session ID is available.
|
|
43
|
+
*/
|
|
44
|
+
launch(reason?: string, conversationId?: number): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Store the Claude resume ID in the DSiloed agent session metadata.
|
|
47
|
+
*/
|
|
48
|
+
private storeResumeId;
|
|
49
|
+
/**
|
|
50
|
+
* Called when an inbound message arrives and no active session can handle it.
|
|
51
|
+
*/
|
|
52
|
+
launchOnMessage(conversationId?: number): Promise<boolean>;
|
|
53
|
+
/**
|
|
54
|
+
* Check for idle sessions — nudge if process alive, respawn if dead.
|
|
55
|
+
* If a session has been nudged multiple times without responding and has
|
|
56
|
+
* pending messages, kill and relaunch it (hung session recovery).
|
|
57
|
+
*/
|
|
58
|
+
private checkIdle;
|
|
59
|
+
/**
|
|
60
|
+
* Monitor a tmux session and detect when it exits.
|
|
61
|
+
*/
|
|
62
|
+
private monitorTmuxSession;
|
|
63
|
+
private checkTmuxSessions;
|
|
64
|
+
/**
|
|
65
|
+
* Send a message to all running Claude sessions via tmux send-keys.
|
|
66
|
+
*/
|
|
67
|
+
nudge(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Kill the Claude session for a specific conversation.
|
|
70
|
+
*/
|
|
71
|
+
killByConversation(conversationId: number): void;
|
|
72
|
+
/**
|
|
73
|
+
* Kill all running Claude Code sessions.
|
|
74
|
+
*/
|
|
75
|
+
kill(): void;
|
|
76
|
+
destroy(): void;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=claude-launcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-launcher.d.ts","sourceRoot":"","sources":["../../src/core/claude-launcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;;;GASG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,gBAAgB,CAA+C;IACvE,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,sBAAsB,CAA0B;IACxD,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,oBAAoB,CAAkC;IAE9D,OAAO,CAAC,mBAAmB,CAAuB;gBAGhD,MAAM,EAAE,cAAc,EACtB,cAAc,EAAE,cAAc,EAC9B,aAAa,CAAC,EAAE,aAAa,EAC7B,YAAY,CAAC,EAAE,YAAY;IAS7B,SAAS,IAAI,OAAO;IAIpB,oBAAoB,IAAI,OAAO;IAI/B,cAAc,IAAI,OAAO;IAIzB,sBAAsB,IAAI,MAAM,GAAG,IAAI;IAIvC,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKvC;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAQxB,eAAe,IAAI,IAAI;IAQvB;;OAEG;IACG,MAAM,CAAC,MAAM,GAAE,MAAiB,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwHlF;;OAEG;YACW,aAAa;IAe3B;;OAEG;IACG,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAehE;;;;OAIG;YACW,SAAS;IAyEvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,iBAAiB;IAgBzB;;OAEG;IACH,KAAK,IAAI,OAAO;IAehB;;OAEG;IACH,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAYhD;;OAEG;IACH,IAAI,IAAI,IAAI;IAgBZ,OAAO,IAAI,IAAI;CAIhB"}
|