@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
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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"}
|