@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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +350 -0
  3. package/dist/api/dsiloed-client.d.ts +32 -0
  4. package/dist/api/dsiloed-client.d.ts.map +1 -0
  5. package/dist/api/dsiloed-client.js +240 -0
  6. package/dist/api/dsiloed-client.js.map +1 -0
  7. package/dist/cable/action-cable-client.d.ts +28 -0
  8. package/dist/cable/action-cable-client.d.ts.map +1 -0
  9. package/dist/cable/action-cable-client.js +194 -0
  10. package/dist/cable/action-cable-client.js.map +1 -0
  11. package/dist/cable/subscription-manager.d.ts +29 -0
  12. package/dist/cable/subscription-manager.d.ts.map +1 -0
  13. package/dist/cable/subscription-manager.js +125 -0
  14. package/dist/cable/subscription-manager.js.map +1 -0
  15. package/dist/cli/commands.d.ts +4 -0
  16. package/dist/cli/commands.d.ts.map +1 -0
  17. package/dist/cli/commands.js +144 -0
  18. package/dist/cli/commands.js.map +1 -0
  19. package/dist/cli/daemon.d.ts +5 -0
  20. package/dist/cli/daemon.d.ts.map +1 -0
  21. package/dist/cli/daemon.js +35 -0
  22. package/dist/cli/daemon.js.map +1 -0
  23. package/dist/config/config-manager.d.ts +7 -0
  24. package/dist/config/config-manager.d.ts.map +1 -0
  25. package/dist/config/config-manager.js +76 -0
  26. package/dist/config/config-manager.js.map +1 -0
  27. package/dist/config/jwt-generator.d.ts +15 -0
  28. package/dist/config/jwt-generator.d.ts.map +1 -0
  29. package/dist/config/jwt-generator.js +54 -0
  30. package/dist/config/jwt-generator.js.map +1 -0
  31. package/dist/core/bridge.d.ts +37 -0
  32. package/dist/core/bridge.d.ts.map +1 -0
  33. package/dist/core/bridge.js +247 -0
  34. package/dist/core/bridge.js.map +1 -0
  35. package/dist/core/claude-launcher.d.ts +78 -0
  36. package/dist/core/claude-launcher.d.ts.map +1 -0
  37. package/dist/core/claude-launcher.js +352 -0
  38. package/dist/core/claude-launcher.js.map +1 -0
  39. package/dist/core/message-queue.d.ts +47 -0
  40. package/dist/core/message-queue.d.ts.map +1 -0
  41. package/dist/core/message-queue.js +139 -0
  42. package/dist/core/message-queue.js.map +1 -0
  43. package/dist/core/session-manager.d.ts +24 -0
  44. package/dist/core/session-manager.d.ts.map +1 -0
  45. package/dist/core/session-manager.js +111 -0
  46. package/dist/core/session-manager.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +4 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/mcp/server.d.ts +27 -0
  52. package/dist/mcp/server.d.ts.map +1 -0
  53. package/dist/mcp/server.js +109 -0
  54. package/dist/mcp/server.js.map +1 -0
  55. package/dist/mcp/tools/register-tools.d.ts +19 -0
  56. package/dist/mcp/tools/register-tools.d.ts.map +1 -0
  57. package/dist/mcp/tools/register-tools.js +385 -0
  58. package/dist/mcp/tools/register-tools.js.map +1 -0
  59. package/dist/types/index.d.ts +74 -0
  60. package/dist/types/index.d.ts.map +1 -0
  61. package/dist/types/index.js +2 -0
  62. package/dist/types/index.js.map +1 -0
  63. package/package.json +57 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 DSiloed
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,350 @@
1
+ # SiloLink — Claude Code Remote Bridge
2
+
3
+ SiloLink is a local daemon that connects Claude Code sessions to [DSiloed](https://www.dsiloed.com) conversations. It provides an MCP server that Claude Code connects to, and maintains a real-time WebSocket connection to DSiloed via ActionCable. Messages flow bidirectionally — you can launch, monitor, interact with, and command Claude Code sessions from the DSiloed web UI, Slack, SMS, or Discord.
4
+
5
+ ```
6
+ ┌──────────────────────┐
7
+ │ DSiloed Server │
8
+ │ │
9
+ Claude Code (tmux) ──MCP──► │ │
10
+ Claude Code (tmux) ──MCP──► SiloLink ──WebSocket──► ActionCable
11
+ Claude Code (tmux) ──MCP──► :3579 ──REST API──► ├── ConversationChannel
12
+ │ ├── ControlChannel
13
+ │ Claude Launcher │
14
+ │ Message Queue │ Web UI / Slack
15
+ │ Session Manager │ Discord / SMS
16
+ └──────────────────────┘
17
+ ```
18
+
19
+ ## Features
20
+
21
+ - **Launch Claude sessions from the web UI** — click "+" in the SiloLinks section to spawn a new session
22
+ - **Auto-launch** — send a message to any SiloLink conversation and Claude starts automatically
23
+ - **Multi-session** — each conversation gets its own Claude Code tmux session
24
+ - **Message buffering** — messages that arrive before Claude registers are queued and delivered
25
+ - **Status notifications** — "Starting session..." and "Session ready!" feedback in the conversation
26
+ - **Agent Dashboard** — active sessions appear in Mission Control with real-time status
27
+ - **Control Channel** — launch/stop/status commands via ActionCable from the UI
28
+ - **Channel linking** — command Claude from Slack, Discord, Teams, or SMS
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ git clone git@gitlab.com:model-api/silo_link.git
34
+ cd silo_link
35
+ pnpm install
36
+ pnpm build
37
+ ```
38
+
39
+ To make the `silolink` command available globally:
40
+
41
+ ```bash
42
+ pnpm link --global
43
+ ```
44
+
45
+ ## Configuration
46
+
47
+ ```bash
48
+ silolink config
49
+ ```
50
+
51
+ Creates `~/.silolink/config.json`:
52
+
53
+ | Field | Description | Default |
54
+ |-------|-------------|---------|
55
+ | `host` | DSiloed server URL | `https://www.dsiloed.com` |
56
+ | `tenant_id` | Your tenant identifier | `harmoniq` |
57
+ | `shared_key` | Tenant shared key for JWT authentication | — |
58
+ | `user_sub` | Your username or email | — |
59
+ | `mcp_port` | Local port for the MCP server | `3579` |
60
+ | `reconnect_interval_ms` | Base reconnect delay | `5000` |
61
+ | `max_reconnect_attempts` | Max reconnect tries | `20` |
62
+ | `claude_command` | Path to Claude CLI binary | `claude` |
63
+ | `claude_working_directory` | Working directory for spawned sessions | — |
64
+ | `claude_auto_respawn` | Auto-respawn on idle/exit | `false` |
65
+ | `claude_idle_timeout_ms` | Idle threshold before respawn | `30000` |
66
+ | `claude_session_prompt` | Default prompt for spawned sessions | `Register with SiloLink...` |
67
+
68
+ **Example with Claude launcher enabled:**
69
+
70
+ ```json
71
+ {
72
+ "host": "https://www.dsiloed.com",
73
+ "tenant_id": "portablemind",
74
+ "shared_key": "your-shared-key",
75
+ "user_sub": "user@example.com",
76
+ "mcp_port": 3579,
77
+ "claude_command": "/home/user/.local/bin/claude",
78
+ "claude_working_directory": "/home/user/projects",
79
+ "claude_auto_respawn": true,
80
+ "claude_idle_timeout_ms": 30000
81
+ }
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ### Starting SiloLink
87
+
88
+ ```bash
89
+ # Foreground (see logs in terminal)
90
+ silolink start
91
+
92
+ # Background (daemon mode)
93
+ silolink start --daemon
94
+ ```
95
+
96
+ ### Managing the Daemon
97
+
98
+ ```bash
99
+ silolink status # Check connection state and active sessions
100
+ silolink sessions # List active sessions
101
+ silolink stop # Stop daemon (kills all Claude tmux sessions)
102
+ ```
103
+
104
+ ### Launching Claude Sessions
105
+
106
+ ```bash
107
+ # Launch from CLI
108
+ silolink launch
109
+ silolink launch -p "Work on the auth refactor"
110
+
111
+ # Launch from the DSiloed web UI
112
+ # Click "+" in the SiloLinks section of TeamChat
113
+ ```
114
+
115
+ ### Connecting Claude Code (Manual)
116
+
117
+ Add SiloLink as an MCP server in your Claude Code settings (`.claude.json` or project `.mcp.json`):
118
+
119
+ ```json
120
+ {
121
+ "mcpServers": {
122
+ "silolink": {
123
+ "command": "npx",
124
+ "args": ["mcp-remote", "http://localhost:3579/mcp"]
125
+ }
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## Claude Session Launcher
131
+
132
+ SiloLink spawns and manages Claude Code sessions using tmux:
133
+
134
+ ### On-Demand (from DSiloed UI)
135
+
136
+ 1. Click **+** in the SiloLinks section of TeamChat
137
+ 2. Enter a session name and optional prompt → **Launch Session**
138
+ 3. Conversation appears immediately — "Starting session..." shows while Claude initializes
139
+ 4. Claude sends "Session ready!" when the poll loop begins (~20-30 seconds)
140
+
141
+ ### Auto-Launch (on inbound message)
142
+
143
+ When a message arrives on a SiloLink conversation with no active session:
144
+ 1. SiloLink posts "Restarting session..." immediately
145
+ 2. Spawns Claude Code in a new tmux session
146
+ 3. The triggering message is buffered and delivered once Claude registers
147
+ 4. Claude sends "Session ready" and enters the poll loop
148
+
149
+ ### Multi-Session
150
+
151
+ Each conversation gets its own tmux session. Multiple sessions run simultaneously.
152
+
153
+ ```bash
154
+ # List active tmux sessions
155
+ tmux list-sessions | grep silolink
156
+
157
+ # Attach to watch Claude work
158
+ tmux attach -t silolink-claude-1774363497579
159
+
160
+ # Detach: Ctrl+B, D
161
+ ```
162
+
163
+ ### Session Lifecycle
164
+
165
+ - **Launch** → SiloLink posts "Starting session..." → spawns tmux → Claude registers → "Session ready!"
166
+ - **Messages** → user sends message → SiloLink enqueues → Claude polls and responds
167
+ - **Delete** → gear icon → "Delete SiloLink" stops the Claude session and deletes the conversation
168
+ - **Shutdown** → `silolink stop` kills all tmux sessions and completes agent sessions on DSiloed
169
+
170
+ ## MCP Tools
171
+
172
+ | Tool | Description | Blocking |
173
+ |------|-------------|----------|
174
+ | `remote_register` | Register session, create/attach conversation | No |
175
+ | `remote_notify` | Fire-and-forget message | No |
176
+ | `remote_ask` | Post question, wait for reply | Yes |
177
+ | `remote_poll` | Non-blocking check for next message (**recommended**) | No |
178
+ | `remote_check_messages` | Non-blocking check for ALL pending messages | No |
179
+ | `remote_wait_for_command` | Block until next message (prefer `remote_poll`) | Yes |
180
+ | `remote_sessions` | List active sessions | No |
181
+ | `remote_unregister` | Unregister and clean up | No |
182
+
183
+ ### remote_register
184
+
185
+ ```
186
+ Input: { session_name?: "my project", conversation_id?: 453 }
187
+ Output: { session_id, conversation_id, conversation_url }
188
+ ```
189
+
190
+ When `conversation_id` is provided, attaches to that existing conversation (used by auto-launcher).
191
+
192
+ ### remote_poll (Recommended)
193
+
194
+ ```
195
+ Input: {}
196
+ Output: { success: true, message: "...", sender_name: "..." }
197
+ — or —
198
+ { success: true, pending: true }
199
+ ```
200
+
201
+ Non-blocking. Safer than `remote_wait_for_command` — can't lose messages on cancellation.
202
+
203
+ ### remote_notify
204
+
205
+ ```
206
+ Input: { message: "Build completed successfully" }
207
+ Output: { success: true, message_id }
208
+ ```
209
+
210
+ ### remote_ask
211
+
212
+ ```
213
+ Input: { question: "Should I proceed?", timeout?: 300 }
214
+ Output: { response: "Yes", sender_name: "rholmes" }
215
+ ```
216
+
217
+ ## Control Channel API
218
+
219
+ SiloLink subscribes to a tenant-scoped ActionCable control channel on startup. The DSiloed UI sends commands via REST:
220
+
221
+ | Endpoint | Method | Description |
222
+ |----------|--------|-------------|
223
+ | `/api/v1/silolink/launch` | POST | Launch new session (creates conversation) |
224
+ | `/api/v1/silolink/stop` | POST | Stop session (by `conversation_id` or all) |
225
+ | `/api/v1/silolink/status` | GET | Request SiloLink status |
226
+
227
+ ```bash
228
+ # Launch
229
+ curl -X POST /api/v1/silolink/launch \
230
+ -d '{"name": "my-feature", "prompt": "Work on auth"}'
231
+
232
+ # Response
233
+ {"success": true, "conversation_id": 458, "message": "Launch command sent"}
234
+ ```
235
+
236
+ ## CLAUDE.md Integration
237
+
238
+ Add to your project's `CLAUDE.md`:
239
+
240
+ ```markdown
241
+ ## Remote Communication (SiloLink)
242
+
243
+ When the SiloLink MCP server is connected:
244
+
245
+ 1. Register with `remote_register({ session_name: "<name>" })`.
246
+ If a conversation_id is provided, pass it too.
247
+ 2. ALL communication MUST go through SiloLink — never write to terminal.
248
+ 3. Send progress updates with `remote_notify()`.
249
+ 4. When idle, use the poll loop:
250
+ - Call `remote_poll()` — returns instantly
251
+ - If `{ pending: true }`: sleep 3s, poll again
252
+ - If message received: process it, notify result, resume
253
+ 5. Use `remote_ask()` for questions needing immediate reply.
254
+ ```
255
+
256
+ ### Poll Loop Pattern
257
+
258
+ ```javascript
259
+ // Register (use conversation_id if provided in launch prompt)
260
+ remote_register({ session_name: "portablemind", conversation_id: 123 })
261
+
262
+ // Poll loop
263
+ while (idle) {
264
+ result = remote_poll()
265
+ if (result.pending) { sleep(3s); continue }
266
+ handle(result.message)
267
+ remote_notify({ message: "Done: ..." })
268
+ }
269
+ ```
270
+
271
+ ## Agent Sessions
272
+
273
+ Active SiloLink sessions appear in the Mission Control Agent Dashboard with:
274
+ - Session name (matches conversation name)
275
+ - Status: idle, working, waiting for human, error, completed
276
+ - Heartbeats on every tool call
277
+ - Conversation link for quick navigation
278
+
279
+ ## Permissions
280
+
281
+ **Option 1 — Skip all prompts:**
282
+ ```bash
283
+ claude --dangerously-skip-permissions
284
+ ```
285
+
286
+ **Option 2 — Allow SiloLink tools only:**
287
+ ```json
288
+ { "permissions": { "allow": ["mcp__silolink__*"] } }
289
+ ```
290
+
291
+ ## Technical Details
292
+
293
+ - **Authentication**: HS256 JWTs (24h validity, auto-refresh every 12h)
294
+ - **Reconnection**: Exponential backoff (1s → 60s cap)
295
+ - **Echo Prevention**: Tracked outbound message IDs + prefix matching
296
+ - **Session Cleanup**: Idle sessions cleaned after 1 hour, agent sessions completed on DSiloed
297
+ - **Multi-Session**: Multiple tmux sessions, one per conversation
298
+ - **Message Buffering**: Pre-registration messages delivered on session register
299
+ - **API Timeouts**: 30-second abort on all DSiloed API calls
300
+ - **Map Validation**: Session manager validates 3-map consistency every 5 minutes
301
+ - **Shutdown**: Kills all Claude tmux sessions first, then completes agent sessions
302
+
303
+ ## Health Check
304
+
305
+ ```bash
306
+ curl http://localhost:3579/health
307
+ # { "status": "ok", "sessions": 2, "cable": "connected" }
308
+ ```
309
+
310
+ ## Development
311
+
312
+ ```bash
313
+ pnpm dev start # Dev mode (no build)
314
+ pnpm exec tsc --noEmit # Type check
315
+ pnpm build # Build
316
+ pnpm test # Tests
317
+ ```
318
+
319
+ ## Project Structure
320
+
321
+ ```
322
+ src/
323
+ index.ts # CLI entry point
324
+ cli/
325
+ commands.ts # CLI commands (start, stop, status, launch, config)
326
+ daemon.ts # PID file management
327
+ config/
328
+ config-manager.ts # ~/.silolink/config.json management
329
+ jwt-generator.ts # HS256 JWT generation + auto-refresh
330
+ core/
331
+ bridge.ts # Main orchestrator — startup, shutdown, control channel
332
+ session-manager.ts # Session registry with 3-map lookup + validation
333
+ message-queue.ts # Per-session inbound queue with acknowledgment protocol
334
+ claude-launcher.ts # tmux-based Claude process management (multi-session)
335
+ cable/
336
+ action-cable-client.ts # ActionCable WebSocket client with auto-reconnect
337
+ subscription-manager.ts # Conversation subscriptions, echo prevention, message buffering
338
+ api/
339
+ dsiloed-client.ts # REST client for DSiloed API (30s timeouts)
340
+ mcp/
341
+ server.ts # MCP server (Streamable HTTP on Express)
342
+ tools/
343
+ register-tools.ts # All 8 MCP tool definitions
344
+ types/
345
+ index.ts # Shared TypeScript interfaces
346
+ ```
347
+
348
+ ## License
349
+
350
+ Private — model-api/silo_link
@@ -0,0 +1,32 @@
1
+ import type { SiloLinkConfig, ConversationResponse, MessageResponse, AgentSessionResponse } from '../types/index.js';
2
+ import type { JwtGenerator } from '../config/jwt-generator.js';
3
+ export declare class DSiloedClient {
4
+ private config;
5
+ private jwt;
6
+ constructor(config: SiloLinkConfig, jwt: JwtGenerator);
7
+ private get headers();
8
+ private url;
9
+ private fetchWithTimeout;
10
+ getSiloLinkConversations(): Promise<ConversationResponse[]>;
11
+ findConversationByName(name: string): Promise<ConversationResponse | null>;
12
+ findOrCreateConversation(name: string, description: string): Promise<ConversationResponse>;
13
+ postMessage(conversationId: number, message: string): Promise<MessageResponse>;
14
+ getMessages(conversationId: number, limit?: number): Promise<MessageResponse[]>;
15
+ getConversation(conversationId: number): Promise<ConversationResponse>;
16
+ createAgentSession(data: {
17
+ session_type: string;
18
+ session_name: string;
19
+ llm_conversation_id?: number;
20
+ metadata?: Record<string, unknown>;
21
+ }): Promise<AgentSessionResponse | null>;
22
+ updateAgentSession(agentSessionId: number, data: {
23
+ metadata?: Record<string, unknown>;
24
+ session_name?: string;
25
+ }): Promise<void>;
26
+ heartbeatAgentSession(agentSessionId: number): Promise<void>;
27
+ completeAgentSession(agentSessionId: number): Promise<void>;
28
+ updateAgentSessionStatus(agentSessionId: number, status: string): Promise<void>;
29
+ getAgentSessionsForConversation(conversationId: number): Promise<AgentSessionResponse[]>;
30
+ getActiveAgentSessions(sessionType?: string): Promise<AgentSessionResponse[]>;
31
+ }
32
+ //# sourceMappingURL=dsiloed-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsiloed-client.d.ts","sourceRoot":"","sources":["../../src/api/dsiloed-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACrH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,GAAG,CAAe;gBACd,MAAM,EAAE,cAAc,EAAE,GAAG,EAAE,YAAY;IAKrD,OAAO,KAAK,OAAO,GAMlB;IAED,OAAO,CAAC,GAAG;YAIG,gBAAgB;IAUxB,wBAAwB,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAqB3D,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAoB1E,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA4B1F,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAe9E,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAqBnF,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkBtE,kBAAkB,CAAC,IAAI,EAAE;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAqBlC,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE;QACrD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,OAAO,CAAC,IAAI,CAAC;IAYX,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW5D,oBAAoB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW3D,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB/E,+BAA+B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAsBxF,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;CAmBpF"}
@@ -0,0 +1,240 @@
1
+ export class DSiloedClient {
2
+ config;
3
+ jwt;
4
+ constructor(config, jwt) {
5
+ this.config = config;
6
+ this.jwt = jwt;
7
+ }
8
+ get headers() {
9
+ return {
10
+ 'Authorization': `Bearer ${this.jwt.getToken()}`,
11
+ 'Tenant-Id': this.config.tenant_id,
12
+ 'Content-Type': 'application/json',
13
+ };
14
+ }
15
+ url(path) {
16
+ return `${this.config.host}${path}`;
17
+ }
18
+ async fetchWithTimeout(url, options = {}) {
19
+ const controller = new AbortController();
20
+ const timeout = setTimeout(() => controller.abort(), 30000);
21
+ try {
22
+ return await fetch(url, { ...options, signal: controller.signal });
23
+ }
24
+ finally {
25
+ clearTimeout(timeout);
26
+ }
27
+ }
28
+ async getSiloLinkConversations() {
29
+ try {
30
+ const params = new URLSearchParams({
31
+ llm_conversation_type_id: 'claude_code_remote',
32
+ });
33
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/llm_conversations?${params}`), {
34
+ method: 'GET',
35
+ headers: this.headers,
36
+ });
37
+ if (!res.ok)
38
+ return [];
39
+ const data = (await res.json());
40
+ return data.llm_conversations || [];
41
+ }
42
+ catch (err) {
43
+ console.error('[SiloLink] Error fetching SiloLink conversations:', err);
44
+ return [];
45
+ }
46
+ }
47
+ async findConversationByName(name) {
48
+ const params = new URLSearchParams({
49
+ llm_conversation_type_id: 'claude_code_remote',
50
+ search_term: name,
51
+ });
52
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/llm_conversations?${params}`), {
53
+ method: 'GET',
54
+ headers: this.headers,
55
+ });
56
+ if (!res.ok)
57
+ return null;
58
+ const data = (await res.json());
59
+ const match = data.llm_conversations?.find((c) => c.name === name);
60
+ return match || null;
61
+ }
62
+ async findOrCreateConversation(name, description) {
63
+ // Try to find existing conversation first
64
+ const existing = await this.findConversationByName(name);
65
+ if (existing)
66
+ return existing;
67
+ // Create new one
68
+ const res = await this.fetchWithTimeout(this.url('/api/v1/llm_conversations'), {
69
+ method: 'POST',
70
+ headers: this.headers,
71
+ body: JSON.stringify({
72
+ llm_conversation: {
73
+ name,
74
+ description,
75
+ llm_conversation_type_id: 'claude_code_remote',
76
+ private: true,
77
+ },
78
+ }),
79
+ });
80
+ if (!res.ok) {
81
+ const body = await res.text();
82
+ throw new Error(`Failed to create conversation (${res.status}): ${body}`);
83
+ }
84
+ const data = (await res.json());
85
+ return data.llm_conversation;
86
+ }
87
+ async postMessage(conversationId, message) {
88
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/llm_conversations/${conversationId}/chat`), {
89
+ method: 'POST',
90
+ headers: this.headers,
91
+ body: JSON.stringify({ message }),
92
+ });
93
+ if (!res.ok) {
94
+ const body = await res.text();
95
+ throw new Error(`Failed to post message (${res.status}): ${body}`);
96
+ }
97
+ return (await res.json());
98
+ }
99
+ async getMessages(conversationId, limit = 50) {
100
+ const params = new URLSearchParams({
101
+ llm_conversation_id: String(conversationId),
102
+ per_page: String(limit),
103
+ });
104
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/llm_messages?${params}`), {
105
+ method: 'GET',
106
+ headers: this.headers,
107
+ });
108
+ if (!res.ok) {
109
+ const body = await res.text();
110
+ throw new Error(`Failed to get messages (${res.status}): ${body}`);
111
+ }
112
+ const data = (await res.json());
113
+ // ModelAPI wraps results in { llm_messages: [...] }
114
+ return (data.llm_messages || data);
115
+ }
116
+ async getConversation(conversationId) {
117
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/llm_conversations/${conversationId}`), {
118
+ method: 'GET',
119
+ headers: this.headers,
120
+ });
121
+ if (!res.ok) {
122
+ const body = await res.text();
123
+ throw new Error(`Failed to get conversation (${res.status}): ${body}`);
124
+ }
125
+ const json = (await res.json());
126
+ // API returns { llm_conversation: {...} } for show action
127
+ return (json.llm_conversation || json);
128
+ }
129
+ // --- Agent Session tracking ---
130
+ async createAgentSession(data) {
131
+ try {
132
+ const res = await this.fetchWithTimeout(this.url('/api/v1/agent_sessions'), {
133
+ method: 'POST',
134
+ headers: this.headers,
135
+ body: JSON.stringify({ agent_session: data }),
136
+ });
137
+ if (!res.ok) {
138
+ console.error(`[SiloLink] Failed to create agent session (${res.status})`);
139
+ return null;
140
+ }
141
+ const json = (await res.json());
142
+ return json.agent_session;
143
+ }
144
+ catch (err) {
145
+ console.error('[SiloLink] Error creating agent session:', err);
146
+ return null;
147
+ }
148
+ }
149
+ async updateAgentSession(agentSessionId, data) {
150
+ try {
151
+ await this.fetchWithTimeout(this.url(`/api/v1/agent_sessions/${agentSessionId}`), {
152
+ method: 'PUT',
153
+ headers: this.headers,
154
+ body: JSON.stringify({ agent_session: data }),
155
+ });
156
+ }
157
+ catch (err) {
158
+ console.error('[SiloLink] Error updating agent session:', err);
159
+ }
160
+ }
161
+ async heartbeatAgentSession(agentSessionId) {
162
+ try {
163
+ await this.fetchWithTimeout(this.url(`/api/v1/agent_sessions/${agentSessionId}/heartbeat`), {
164
+ method: 'POST',
165
+ headers: this.headers,
166
+ });
167
+ }
168
+ catch (err) {
169
+ console.error('[SiloLink] Error heartbeating agent session:', err);
170
+ }
171
+ }
172
+ async completeAgentSession(agentSessionId) {
173
+ try {
174
+ await this.fetchWithTimeout(this.url(`/api/v1/agent_sessions/${agentSessionId}/complete`), {
175
+ method: 'POST',
176
+ headers: this.headers,
177
+ });
178
+ }
179
+ catch (err) {
180
+ console.error('[SiloLink] Error completing agent session:', err);
181
+ }
182
+ }
183
+ async updateAgentSessionStatus(agentSessionId, status) {
184
+ try {
185
+ // Apply status via the status_applications endpoint
186
+ await this.fetchWithTimeout(this.url('/api/v1/status_applications/update_current_status'), {
187
+ method: 'PUT',
188
+ headers: this.headers,
189
+ body: JSON.stringify({
190
+ tracked_entity_type: 'AgentSession',
191
+ tracked_entity_id: agentSessionId,
192
+ status,
193
+ }),
194
+ });
195
+ }
196
+ catch (err) {
197
+ console.error('[SiloLink] Error updating agent session status:', err);
198
+ }
199
+ }
200
+ async getAgentSessionsForConversation(conversationId) {
201
+ try {
202
+ const params = new URLSearchParams({
203
+ llm_conversation_id: String(conversationId),
204
+ session_type: 'silolink',
205
+ });
206
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/agent_sessions?${params}`), {
207
+ method: 'GET',
208
+ headers: this.headers,
209
+ });
210
+ if (!res.ok)
211
+ return [];
212
+ const json = (await res.json());
213
+ return json.agent_sessions || [];
214
+ }
215
+ catch (err) {
216
+ console.error('[SiloLink] Error fetching agent sessions for conversation:', err);
217
+ return [];
218
+ }
219
+ }
220
+ async getActiveAgentSessions(sessionType) {
221
+ try {
222
+ const params = new URLSearchParams({ active_only: 'true' });
223
+ if (sessionType)
224
+ params.set('session_type', sessionType);
225
+ const res = await this.fetchWithTimeout(this.url(`/api/v1/agent_sessions/active?${params}`), {
226
+ method: 'GET',
227
+ headers: this.headers,
228
+ });
229
+ if (!res.ok)
230
+ return [];
231
+ const json = (await res.json());
232
+ return json.agent_sessions || [];
233
+ }
234
+ catch (err) {
235
+ console.error('[SiloLink] Error fetching active agent sessions:', err);
236
+ return [];
237
+ }
238
+ }
239
+ }
240
+ //# sourceMappingURL=dsiloed-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsiloed-client.js","sourceRoot":"","sources":["../../src/api/dsiloed-client.ts"],"names":[],"mappings":"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"}