@delt/claude-alarm 0.1.7 → 0.1.8
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/README.md +181 -181
- package/dist/channel/server.js.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/index.html +674 -674
- package/dist/hub/server.js +4 -0
- package/dist/hub/server.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +54 -54
- package/src/dashboard/index.html +674 -674
package/README.md
CHANGED
|
@@ -1,181 +1,181 @@
|
|
|
1
|
-
# claude-alarm
|
|
2
|
-
|
|
3
|
-
Monitor and interact with multiple Claude Code sessions from a web dashboard. Get desktop notifications when tasks complete, send messages to Claude, and track session status — all through MCP Channels.
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
[Dashboard] ──message──> [Hub Server] ──WebSocket──> [Channel Server] ──> Claude Code
|
|
7
|
-
<──
|
|
8
|
-
Claude Code ──reply/notify──> [Channel Server] ──> [Hub Server] ──> [Dashboard]
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Features
|
|
12
|
-
|
|
13
|
-
- **Web Dashboard** — Monitor all Claude Code sessions in one place
|
|
14
|
-
- **Two-way Messaging** — Send messages to Claude and receive replies (with markdown rendering)
|
|
15
|
-
- **Desktop Notifications** — Get Windows/macOS/Linux toast notifications
|
|
16
|
-
- **Session Status** — See which sessions are idle, working, or waiting for input
|
|
17
|
-
- **Token Auth** — Secure hub access with auto-generated tokens
|
|
18
|
-
- **Multi-session** — Connect multiple Claude Code instances simultaneously
|
|
19
|
-
|
|
20
|
-
## Quick Start
|
|
21
|
-
|
|
22
|
-
### 1. Install & Initialize
|
|
23
|
-
|
|
24
|
-
In your project directory:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npx @delt/claude-alarm init
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
This creates `.mcp.json` with the claude-alarm channel server config.
|
|
31
|
-
|
|
32
|
-
### 2. Start the Hub
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npx @delt/claude-alarm hub start
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
This starts the hub server and prints your auth token.
|
|
39
|
-
|
|
40
|
-
### 3. Run Claude Code
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
claude --dangerously-load-development-channels server:claude-alarm
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
To allow remote control without approval prompts:
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
> **WARNING:** `--dangerously-skip-permissions` allows Claude to execute any action without your approval. Only use in trusted, isolated environments.
|
|
53
|
-
|
|
54
|
-
### 4. Open Dashboard
|
|
55
|
-
|
|
56
|
-
Open `http://127.0.0.1:7890` in your browser.
|
|
57
|
-
|
|
58
|
-
## CLI Commands
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
claude-alarm init Setup everything and show next steps
|
|
62
|
-
claude-alarm hub start [-d] Start the hub server (-d for daemon mode)
|
|
63
|
-
claude-alarm hub stop Stop the hub daemon
|
|
64
|
-
claude-alarm hub status Show hub status
|
|
65
|
-
claude-alarm setup [dir] Add claude-alarm to .mcp.json
|
|
66
|
-
claude-alarm test Send a test notification
|
|
67
|
-
claude-alarm token Show current auth token
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## How It Works
|
|
71
|
-
|
|
72
|
-
claude-alarm uses [MCP Channels](https://modelcontextprotocol.io) to create a communication bridge between Claude Code and a web dashboard.
|
|
73
|
-
|
|
74
|
-
- **Hub Server** — Central server that manages sessions, serves the dashboard, and routes messages
|
|
75
|
-
- **Channel Server** — MCP server that runs inside Claude Code, providing tools and forwarding messages
|
|
76
|
-
- **Dashboard** — Web UI for monitoring sessions and sending messages
|
|
77
|
-
|
|
78
|
-
### Tools Available to Claude
|
|
79
|
-
|
|
80
|
-
| Tool | Description |
|
|
81
|
-
|------|-------------|
|
|
82
|
-
| `notify` | Send a desktop notification (title, message, level) |
|
|
83
|
-
| `reply` | Send a message to the dashboard |
|
|
84
|
-
| `status` | Update session status (idle, working, waiting_input) |
|
|
85
|
-
|
|
86
|
-
## Configuration
|
|
87
|
-
|
|
88
|
-
Config is stored at `~/.claude-alarm/config.json`:
|
|
89
|
-
|
|
90
|
-
```json
|
|
91
|
-
{
|
|
92
|
-
"hub": {
|
|
93
|
-
"host": "127.0.0.1",
|
|
94
|
-
"port": 7890,
|
|
95
|
-
"token": "auto-generated-uuid"
|
|
96
|
-
},
|
|
97
|
-
"notifications": {
|
|
98
|
-
"desktop": true,
|
|
99
|
-
"sound": true
|
|
100
|
-
},
|
|
101
|
-
"webhooks": []
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Custom Session Names
|
|
106
|
-
|
|
107
|
-
The `.mcp.json` created by `claude-alarm init` automatically uses the project directory name as the session name. You can customize it:
|
|
108
|
-
|
|
109
|
-
```json
|
|
110
|
-
{
|
|
111
|
-
"mcpServers": {
|
|
112
|
-
"claude-alarm": {
|
|
113
|
-
"command": "npx",
|
|
114
|
-
"args": ["-y", "@delt/claude-alarm", "serve"],
|
|
115
|
-
"env": {
|
|
116
|
-
"CLAUDE_ALARM_SESSION_NAME": "my-project"
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Webhooks
|
|
124
|
-
|
|
125
|
-
Send notifications to external services:
|
|
126
|
-
|
|
127
|
-
```json
|
|
128
|
-
{
|
|
129
|
-
"webhooks": [
|
|
130
|
-
{
|
|
131
|
-
"url": "https://hooks.slack.com/services/...",
|
|
132
|
-
"headers": { "Content-Type": "application/json" }
|
|
133
|
-
}
|
|
134
|
-
]
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Remote Access
|
|
139
|
-
|
|
140
|
-
To access the hub from another machine:
|
|
141
|
-
|
|
142
|
-
1. Set host to `0.0.0.0` in `~/.claude-alarm/config.json`
|
|
143
|
-
2. Open port 7890 in your firewall
|
|
144
|
-
3. On the remote machine, run `claude-alarm init` and select remote hub (Y), or manually set `.mcp.json`:
|
|
145
|
-
|
|
146
|
-
```json
|
|
147
|
-
{
|
|
148
|
-
"mcpServers": {
|
|
149
|
-
"claude-alarm": {
|
|
150
|
-
"command": "npx",
|
|
151
|
-
"args": ["-y", "@delt/claude-alarm", "serve"],
|
|
152
|
-
"env": {
|
|
153
|
-
"CLAUDE_ALARM_HUB_HOST": "your-server-ip",
|
|
154
|
-
"CLAUDE_ALARM_HUB_PORT": "7890",
|
|
155
|
-
"CLAUDE_ALARM_HUB_TOKEN": "your-token"
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Platform Support
|
|
163
|
-
|
|
164
|
-
Desktop notifications work across all major platforms via [node-notifier](https://github.com/mikaelbr/node-notifier):
|
|
165
|
-
|
|
166
|
-
| Platform | Notification Engine | Click to Open |
|
|
167
|
-
|----------|-------------------|---------------|
|
|
168
|
-
| Windows | SnoreToast (built-in) | `Start-Process` |
|
|
169
|
-
| macOS | terminal-notifier | `open` |
|
|
170
|
-
| Linux | notify-send (libnotify) | `xdg-open` |
|
|
171
|
-
|
|
172
|
-
No additional setup needed — `node-notifier` automatically detects your OS and uses the appropriate tool.
|
|
173
|
-
|
|
174
|
-
## Requirements
|
|
175
|
-
|
|
176
|
-
- Node.js >= 18
|
|
177
|
-
- Claude Code with MCP Channels support
|
|
178
|
-
|
|
179
|
-
## License
|
|
180
|
-
|
|
181
|
-
MIT
|
|
1
|
+
# claude-alarm
|
|
2
|
+
|
|
3
|
+
Monitor and interact with multiple Claude Code sessions from a web dashboard. Get desktop notifications when tasks complete, send messages to Claude, and track session status — all through MCP Channels.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
[Dashboard] ──message──> [Hub Server] ──WebSocket──> [Channel Server] ──> Claude Code
|
|
7
|
+
<──
|
|
8
|
+
Claude Code ──reply/notify──> [Channel Server] ──> [Hub Server] ──> [Dashboard]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Web Dashboard** — Monitor all Claude Code sessions in one place
|
|
14
|
+
- **Two-way Messaging** — Send messages to Claude and receive replies (with markdown rendering)
|
|
15
|
+
- **Desktop Notifications** — Get Windows/macOS/Linux toast notifications
|
|
16
|
+
- **Session Status** — See which sessions are idle, working, or waiting for input
|
|
17
|
+
- **Token Auth** — Secure hub access with auto-generated tokens
|
|
18
|
+
- **Multi-session** — Connect multiple Claude Code instances simultaneously
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Install & Initialize
|
|
23
|
+
|
|
24
|
+
In your project directory:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx @delt/claude-alarm init
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This creates `.mcp.json` with the claude-alarm channel server config.
|
|
31
|
+
|
|
32
|
+
### 2. Start the Hub
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx @delt/claude-alarm hub start
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This starts the hub server and prints your auth token.
|
|
39
|
+
|
|
40
|
+
### 3. Run Claude Code
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
claude --dangerously-load-development-channels server:claude-alarm
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
To allow remote control without approval prompts:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
claude --dangerously-load-development-channels server:claude-alarm --dangerously-skip-permissions
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> **WARNING:** `--dangerously-skip-permissions` allows Claude to execute any action without your approval. Only use in trusted, isolated environments.
|
|
53
|
+
|
|
54
|
+
### 4. Open Dashboard
|
|
55
|
+
|
|
56
|
+
Open `http://127.0.0.1:7890` in your browser.
|
|
57
|
+
|
|
58
|
+
## CLI Commands
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
claude-alarm init Setup everything and show next steps
|
|
62
|
+
claude-alarm hub start [-d] Start the hub server (-d for daemon mode)
|
|
63
|
+
claude-alarm hub stop Stop the hub daemon
|
|
64
|
+
claude-alarm hub status Show hub status
|
|
65
|
+
claude-alarm setup [dir] Add claude-alarm to .mcp.json
|
|
66
|
+
claude-alarm test Send a test notification
|
|
67
|
+
claude-alarm token Show current auth token
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## How It Works
|
|
71
|
+
|
|
72
|
+
claude-alarm uses [MCP Channels](https://modelcontextprotocol.io) to create a communication bridge between Claude Code and a web dashboard.
|
|
73
|
+
|
|
74
|
+
- **Hub Server** — Central server that manages sessions, serves the dashboard, and routes messages
|
|
75
|
+
- **Channel Server** — MCP server that runs inside Claude Code, providing tools and forwarding messages
|
|
76
|
+
- **Dashboard** — Web UI for monitoring sessions and sending messages
|
|
77
|
+
|
|
78
|
+
### Tools Available to Claude
|
|
79
|
+
|
|
80
|
+
| Tool | Description |
|
|
81
|
+
|------|-------------|
|
|
82
|
+
| `notify` | Send a desktop notification (title, message, level) |
|
|
83
|
+
| `reply` | Send a message to the dashboard |
|
|
84
|
+
| `status` | Update session status (idle, working, waiting_input) |
|
|
85
|
+
|
|
86
|
+
## Configuration
|
|
87
|
+
|
|
88
|
+
Config is stored at `~/.claude-alarm/config.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"hub": {
|
|
93
|
+
"host": "127.0.0.1",
|
|
94
|
+
"port": 7890,
|
|
95
|
+
"token": "auto-generated-uuid"
|
|
96
|
+
},
|
|
97
|
+
"notifications": {
|
|
98
|
+
"desktop": true,
|
|
99
|
+
"sound": true
|
|
100
|
+
},
|
|
101
|
+
"webhooks": []
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Custom Session Names
|
|
106
|
+
|
|
107
|
+
The `.mcp.json` created by `claude-alarm init` automatically uses the project directory name as the session name. You can customize it:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"claude-alarm": {
|
|
113
|
+
"command": "npx",
|
|
114
|
+
"args": ["-y", "@delt/claude-alarm", "serve"],
|
|
115
|
+
"env": {
|
|
116
|
+
"CLAUDE_ALARM_SESSION_NAME": "my-project"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Webhooks
|
|
124
|
+
|
|
125
|
+
Send notifications to external services:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"webhooks": [
|
|
130
|
+
{
|
|
131
|
+
"url": "https://hooks.slack.com/services/...",
|
|
132
|
+
"headers": { "Content-Type": "application/json" }
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Remote Access
|
|
139
|
+
|
|
140
|
+
To access the hub from another machine:
|
|
141
|
+
|
|
142
|
+
1. Set host to `0.0.0.0` in `~/.claude-alarm/config.json`
|
|
143
|
+
2. Open port 7890 in your firewall
|
|
144
|
+
3. On the remote machine, run `claude-alarm init` and select remote hub (Y), or manually set `.mcp.json`:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"mcpServers": {
|
|
149
|
+
"claude-alarm": {
|
|
150
|
+
"command": "npx",
|
|
151
|
+
"args": ["-y", "@delt/claude-alarm", "serve"],
|
|
152
|
+
"env": {
|
|
153
|
+
"CLAUDE_ALARM_HUB_HOST": "your-server-ip",
|
|
154
|
+
"CLAUDE_ALARM_HUB_PORT": "7890",
|
|
155
|
+
"CLAUDE_ALARM_HUB_TOKEN": "your-token"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Platform Support
|
|
163
|
+
|
|
164
|
+
Desktop notifications work across all major platforms via [node-notifier](https://github.com/mikaelbr/node-notifier):
|
|
165
|
+
|
|
166
|
+
| Platform | Notification Engine | Click to Open |
|
|
167
|
+
|----------|-------------------|---------------|
|
|
168
|
+
| Windows | SnoreToast (built-in) | `Start-Process` |
|
|
169
|
+
| macOS | terminal-notifier | `open` |
|
|
170
|
+
| Linux | notify-send (libnotify) | `xdg-open` |
|
|
171
|
+
|
|
172
|
+
No additional setup needed — `node-notifier` automatically detects your OS and uses the appropriate tool.
|
|
173
|
+
|
|
174
|
+
## Requirements
|
|
175
|
+
|
|
176
|
+
- Node.js >= 18
|
|
177
|
+
- Claude Code with MCP Channels support
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/channel/server.ts","../../src/shared/logger.ts","../../src/shared/constants.ts","../../src/shared/config.ts","../../src/channel/hub-client.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { randomUUID } from 'node:crypto';\nimport { logger } from '../shared/logger.js';\nimport { CHANNEL_SERVER_NAME, CHANNEL_SERVER_VERSION } from '../shared/constants.js';\nimport { loadConfig } from '../shared/config.js';\nimport { HubClient } from './hub-client.js';\nimport type { SessionStatus, NotifyLevel } from '../shared/types.js';\n\nconst sessionId = randomUUID();\nconst sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? `session-${sessionId.slice(0, 8)}`;\n\nconst server = new Server(\n {\n name: CHANNEL_SERVER_NAME,\n version: CHANNEL_SERVER_VERSION,\n },\n {\n capabilities: {\n experimental: { 'claude/channel': {} },\n tools: {},\n },\n instructions:\n 'Messages from the claude-alarm dashboard arrive as <channel source=\"claude-alarm\" sender=\"...\">. ' +\n 'Read the message and act on it. Reply with the same detail and depth as you normally would — do not shorten your response. ' +\n 'To reply, call the reply tool with the message content. ' +\n 'Use the notify tool to send desktop notifications. Use the status tool to update your session status.',\n },\n);\n\n// Load config for hub connection (env vars take priority)\nconst config = loadConfig();\nconst hubHost = process.env.CLAUDE_ALARM_HUB_HOST ?? config.hub.host;\nconst hubPort = process.env.CLAUDE_ALARM_HUB_PORT ? parseInt(process.env.CLAUDE_ALARM_HUB_PORT, 10) : config.hub.port;\nconst hubToken = process.env.CLAUDE_ALARM_HUB_TOKEN ?? config.hub.token;\n\n// Hub client for forwarding to central hub\nconst hubClient = new HubClient(\n sessionId,\n sessionName,\n hubHost,\n hubPort,\n hubToken,\n);\n\n// --- Tools ---\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n {\n name: 'notify',\n description:\n 'Send a desktop notification to the user. Use this when you complete a task, encounter an error, or need user attention. The notification will appear as a system toast/popup.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n title: { type: 'string', description: 'Notification title (short)' },\n message: { type: 'string', description: 'Notification body text' },\n level: {\n type: 'string',\n enum: ['info', 'warning', 'error', 'success'],\n description: 'Notification level (default: info)',\n },\n },\n required: ['title', 'message'],\n },\n },\n {\n name: 'reply',\n description:\n 'Send a message to the web dashboard. Use this to communicate status updates, results, or any information the user should see in the monitoring dashboard.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n content: { type: 'string', description: 'Message content to display on the dashboard' },\n },\n required: ['content'],\n },\n },\n {\n name: 'status',\n description:\n 'Update your session status displayed on the dashboard. Set to \"working\" when actively processing, \"waiting_input\" when you need user input, or \"idle\" when done.',\n inputSchema: {\n type: 'object' as const,\n properties: {\n status: {\n type: 'string',\n enum: ['idle', 'working', 'waiting_input'],\n description: 'Current session status',\n },\n },\n required: ['status'],\n },\n },\n ],\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n switch (name) {\n case 'notify': {\n const title = args?.title as string;\n const message = args?.message as string;\n const level = (args?.level as NotifyLevel) ?? 'info';\n logger.info(`Notify [${level}]: ${title} - ${message}`);\n hubClient.send({\n type: 'notify',\n sessionId,\n title,\n message,\n level,\n });\n return {\n content: [{ type: 'text', text: `Notification sent: \"${title}\"` }],\n };\n }\n\n case 'reply': {\n const content = args?.content as string;\n logger.info(`Reply: ${content.slice(0, 100)}...`);\n hubClient.send({\n type: 'reply',\n sessionId,\n content,\n });\n return {\n content: [{ type: 'text', text: 'Message sent to dashboard.' }],\n };\n }\n\n case 'status': {\n const status = args?.status as SessionStatus;\n logger.info(`Status update: ${status}`);\n hubClient.send({\n type: 'status',\n sessionId,\n status,\n });\n return {\n content: [{ type: 'text', text: `Status updated to \"${status}\".` }],\n };\n }\n\n default:\n return {\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\n isError: true,\n };\n }\n});\n\n// --- Startup ---\n\nasync function main() {\n logger.info(`Starting MCP channel server (session: ${sessionId})`);\n\n // Connect to hub (non-blocking, will retry)\n hubClient.connect();\n\n // Listen for messages from hub and forward to Claude via channel notification\n hubClient.onMessage(async (msg) => {\n if (msg.type === 'message_to_session' && msg.sessionId === sessionId) {\n logger.info(`Message from dashboard: ${msg.content}`);\n await server.notification({\n method: 'notifications/claude/channel',\n params: {\n content: msg.content,\n meta: { sender: 'dashboard', timestamp: String(Date.now()) },\n },\n });\n }\n });\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info('MCP channel server running on stdio');\n}\n\nmain().catch((err) => {\n logger.error('Fatal error:', err);\n process.exit(1);\n});\n","/**\n * Logger that writes to stderr only.\n * CRITICAL: In MCP channel servers, stdout is used for the stdio protocol.\n * Any console.log() would corrupt the protocol. Always use this logger.\n */\nexport const logger = {\n info(msg: string, ...args: unknown[]) {\n console.error(`[claude-alarm] ${msg}`, ...args);\n },\n warn(msg: string, ...args: unknown[]) {\n console.error(`[claude-alarm WARN] ${msg}`, ...args);\n },\n error(msg: string, ...args: unknown[]) {\n console.error(`[claude-alarm ERROR] ${msg}`, ...args);\n },\n debug(msg: string, ...args: unknown[]) {\n if (process.env.CLAUDE_ALARM_DEBUG) {\n console.error(`[claude-alarm DEBUG] ${msg}`, ...args);\n }\n },\n};\n","import path from 'node:path';\nimport os from 'node:os';\n\nexport const DEFAULT_HUB_HOST = '127.0.0.1';\nexport const DEFAULT_HUB_PORT = 7890;\n\nexport const CONFIG_DIR = path.join(os.homedir(), '.claude-alarm');\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\nexport const PID_FILE = path.join(CONFIG_DIR, 'hub.pid');\nexport const LOG_FILE = path.join(CONFIG_DIR, 'hub.log');\n\nexport const WS_PATH_CHANNEL = '/ws/channel';\nexport const WS_PATH_DASHBOARD = '/ws/dashboard';\n\nexport const CHANNEL_SERVER_NAME = 'claude-alarm';\nexport const CHANNEL_SERVER_VERSION = '0.1.0';\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { CONFIG_DIR, CONFIG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './constants.js';\nimport type { AppConfig } from './types.js';\n\nconst DEFAULT_CONFIG: AppConfig = {\n hub: {\n host: DEFAULT_HUB_HOST,\n port: DEFAULT_HUB_PORT,\n },\n notifications: {\n desktop: true,\n sound: true,\n },\n webhooks: [],\n};\n\nexport function ensureConfigDir(): void {\n if (!fs.existsSync(CONFIG_DIR)) {\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\n }\n}\n\nexport function loadConfig(): AppConfig {\n ensureConfigDir();\n let config: AppConfig;\n if (!fs.existsSync(CONFIG_FILE)) {\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\n } else {\n try {\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\n const parsed = JSON.parse(raw);\n config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub } };\n } catch {\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\n }\n }\n\n // Auto-generate token if missing\n if (!config.hub.token) {\n config.hub.token = randomUUID();\n saveConfig(config);\n }\n\n return config;\n}\n\n/** Get the current token, generating one if needed */\nexport function getOrCreateToken(): string {\n const config = loadConfig();\n return config.hub.token!;\n}\n\nexport function saveConfig(config: AppConfig): void {\n ensureConfigDir();\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });\n}\n\n/**\n * Add claude-alarm as an MCP channel server to .mcp.json\n */\nexport function setupMcpConfig(targetDir?: string): string {\n const dir = targetDir ?? process.cwd();\n const mcpPath = path.join(dir, '.mcp.json');\n\n let mcpConfig: Record<string, any> = {};\n if (fs.existsSync(mcpPath)) {\n try {\n mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\n } catch {\n mcpConfig = {};\n }\n }\n\n if (!mcpConfig.mcpServers) {\n mcpConfig.mcpServers = {};\n }\n\n mcpConfig.mcpServers['claude-alarm'] = {\n command: 'npx',\n args: ['-y', '@delt/claude-alarm', 'serve'],\n env: {\n CLAUDE_ALARM_SESSION_NAME: path.basename(dir),\n },\n };\n\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\n return mcpPath;\n}\n","import WebSocket from 'ws';\nimport { logger } from '../shared/logger.js';\nimport { DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, WS_PATH_CHANNEL } from '../shared/constants.js';\nimport type { ChannelMessage, SessionInfo } from '../shared/types.js';\n\nexport class HubClient {\n private ws: WebSocket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private messageHandlers: Array<(msg: ChannelMessage) => void> = [];\n private queue: ChannelMessage[] = [];\n private connected = false;\n\n constructor(\n private sessionId: string,\n private sessionName: string,\n private hubHost = DEFAULT_HUB_HOST,\n private hubPort = DEFAULT_HUB_PORT,\n private token?: string,\n ) {}\n\n connect(): void {\n const tokenQuery = this.token ? `?token=${encodeURIComponent(this.token)}` : '';\n const url = `ws://${this.hubHost}:${this.hubPort}${WS_PATH_CHANNEL}${tokenQuery}`;\n logger.debug(`Connecting to hub at ${url}`);\n\n try {\n this.ws = new WebSocket(url);\n\n this.ws.on('open', () => {\n logger.info('Connected to hub');\n this.connected = true;\n\n // Register this session\n const registration: ChannelMessage = {\n type: 'register',\n session: {\n id: this.sessionId,\n name: this.sessionName,\n status: 'idle',\n connectedAt: Date.now(),\n lastActivity: Date.now(),\n cwd: process.cwd(),\n channelEnabled: true,\n },\n };\n this.ws!.send(JSON.stringify(registration));\n\n // Flush queued messages\n for (const msg of this.queue) {\n this.ws!.send(JSON.stringify(msg));\n }\n this.queue = [];\n });\n\n this.ws.on('message', (data) => {\n try {\n const msg = JSON.parse(data.toString()) as ChannelMessage;\n for (const handler of this.messageHandlers) {\n handler(msg);\n }\n } catch (err) {\n logger.warn('Failed to parse hub message:', err);\n }\n });\n\n this.ws.on('close', () => {\n logger.info('Disconnected from hub');\n this.connected = false;\n this.scheduleReconnect();\n });\n\n this.ws.on('error', (err) => {\n logger.debug(`Hub connection error: ${err.message}`);\n this.connected = false;\n });\n } catch {\n logger.debug('Failed to connect to hub, will retry');\n this.scheduleReconnect();\n }\n }\n\n send(msg: ChannelMessage): void {\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\n this.ws.send(JSON.stringify(msg));\n } else {\n if (this.queue.length < 100) {\n this.queue.push(msg);\n }\n logger.debug('Hub not connected, message queued');\n }\n }\n\n onMessage(handler: (msg: ChannelMessage) => void): void {\n this.messageHandlers.push(handler);\n }\n\n disconnect(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n this.connected = false;\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connect();\n }, 5000);\n }\n}\n"],"mappings":";;;AAAA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAAA,mBAAkB;;;ACDpB,IAAM,SAAS;AAAA,EACpB,KAAK,QAAgB,MAAiB;AACpC,YAAQ,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI;AAAA,EAChD;AAAA,EACA,KAAK,QAAgB,MAAiB;AACpC,YAAQ,MAAM,uBAAuB,GAAG,IAAI,GAAG,IAAI;AAAA,EACrD;AAAA,EACA,MAAM,QAAgB,MAAiB;AACrC,YAAQ,MAAM,wBAAwB,GAAG,IAAI,GAAG,IAAI;AAAA,EACtD;AAAA,EACA,MAAM,QAAgB,MAAiB;AACrC,QAAI,QAAQ,IAAI,oBAAoB;AAClC,cAAQ,MAAM,wBAAwB,GAAG,IAAI,GAAG,IAAI;AAAA,IACtD;AAAA,EACF;AACF;;;ACpBA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAER,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAEzB,IAAM,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,eAAe;AAC1D,IAAM,cAAc,KAAK,KAAK,YAAY,aAAa;AACvD,IAAM,WAAW,KAAK,KAAK,YAAY,SAAS;AAChD,IAAM,WAAW,KAAK,KAAK,YAAY,SAAS;AAEhD,IAAM,kBAAkB;AAGxB,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;;;ACftC,OAAO,QAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,kBAAkB;AAI3B,IAAM,iBAA4B;AAAA,EAChC,KAAK;AAAA,IACH,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AAAA,EACA,eAAe;AAAA,IACb,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,UAAU,CAAC;AACb;AAEO,SAAS,kBAAwB;AACtC,MAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC9B,OAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAAA,EAC9C;AACF;AAEO,SAAS,aAAwB;AACtC,kBAAgB;AAChB,MAAIC;AACJ,MAAI,CAAC,GAAG,WAAW,WAAW,GAAG;AAC/B,IAAAA,UAAS,EAAE,GAAG,gBAAgB,KAAK,EAAE,GAAG,eAAe,IAAI,EAAE;AAAA,EAC/D,OAAO;AACL,QAAI;AACF,YAAM,MAAM,GAAG,aAAa,aAAa,OAAO;AAChD,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,MAAAA,UAAS,EAAE,GAAG,gBAAgB,GAAG,QAAQ,KAAK,EAAE,GAAG,eAAe,KAAK,GAAG,OAAO,IAAI,EAAE;AAAA,IACzF,QAAQ;AACN,MAAAA,UAAS,EAAE,GAAG,gBAAgB,KAAK,EAAE,GAAG,eAAe,IAAI,EAAE;AAAA,IAC/D;AAAA,EACF;AAGA,MAAI,CAACA,QAAO,IAAI,OAAO;AACrB,IAAAA,QAAO,IAAI,QAAQ,WAAW;AAC9B,eAAWA,OAAM;AAAA,EACnB;AAEA,SAAOA;AACT;AAQO,SAAS,WAAWC,SAAyB;AAClD,kBAAgB;AAChB,KAAG,cAAc,aAAa,KAAK,UAAUA,SAAQ,MAAM,CAAC,GAAG,EAAE,UAAU,SAAS,MAAM,IAAM,CAAC;AACnG;;;ACzDA,OAAO,eAAe;AAKf,IAAM,YAAN,MAAgB;AAAA,EAOrB,YACUC,YACAC,cACAC,WAAU,kBACVC,WAAU,kBACV,OACR;AALQ,qBAAAH;AACA,uBAAAC;AACA,mBAAAC;AACA,mBAAAC;AACA;AAAA,EACP;AAAA,EAZK,KAAuB;AAAA,EACvB,iBAAuD;AAAA,EACvD,kBAAwD,CAAC;AAAA,EACzD,QAA0B,CAAC;AAAA,EAC3B,YAAY;AAAA,EAUpB,UAAgB;AACd,UAAM,aAAa,KAAK,QAAQ,UAAU,mBAAmB,KAAK,KAAK,CAAC,KAAK;AAC7E,UAAM,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,OAAO,GAAG,eAAe,GAAG,UAAU;AAC/E,WAAO,MAAM,wBAAwB,GAAG,EAAE;AAE1C,QAAI;AACF,WAAK,KAAK,IAAI,UAAU,GAAG;AAE3B,WAAK,GAAG,GAAG,QAAQ,MAAM;AACvB,eAAO,KAAK,kBAAkB;AAC9B,aAAK,YAAY;AAGjB,cAAM,eAA+B;AAAA,UACnC,MAAM;AAAA,UACN,SAAS;AAAA,YACP,IAAI,KAAK;AAAA,YACT,MAAM,KAAK;AAAA,YACX,QAAQ;AAAA,YACR,aAAa,KAAK,IAAI;AAAA,YACtB,cAAc,KAAK,IAAI;AAAA,YACvB,KAAK,QAAQ,IAAI;AAAA,YACjB,gBAAgB;AAAA,UAClB;AAAA,QACF;AACA,aAAK,GAAI,KAAK,KAAK,UAAU,YAAY,CAAC;AAG1C,mBAAW,OAAO,KAAK,OAAO;AAC5B,eAAK,GAAI,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,QACnC;AACA,aAAK,QAAQ,CAAC;AAAA,MAChB,CAAC;AAED,WAAK,GAAG,GAAG,WAAW,CAAC,SAAS;AAC9B,YAAI;AACF,gBAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AACtC,qBAAW,WAAW,KAAK,iBAAiB;AAC1C,oBAAQ,GAAG;AAAA,UACb;AAAA,QACF,SAAS,KAAK;AACZ,iBAAO,KAAK,gCAAgC,GAAG;AAAA,QACjD;AAAA,MACF,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,MAAM;AACxB,eAAO,KAAK,uBAAuB;AACnC,aAAK,YAAY;AACjB,aAAK,kBAAkB;AAAA,MACzB,CAAC;AAED,WAAK,GAAG,GAAG,SAAS,CAAC,QAAQ;AAC3B,eAAO,MAAM,yBAAyB,IAAI,OAAO,EAAE;AACnD,aAAK,YAAY;AAAA,MACnB,CAAC;AAAA,IACH,QAAQ;AACN,aAAO,MAAM,sCAAsC;AACnD,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,KAAK,KAA2B;AAC9B,QAAI,KAAK,aAAa,KAAK,IAAI,eAAe,UAAU,MAAM;AAC5D,WAAK,GAAG,KAAK,KAAK,UAAU,GAAG,CAAC;AAAA,IAClC,OAAO;AACL,UAAI,KAAK,MAAM,SAAS,KAAK;AAC3B,aAAK,MAAM,KAAK,GAAG;AAAA,MACrB;AACA,aAAO,MAAM,mCAAmC;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,UAAU,SAA8C;AACtD,SAAK,gBAAgB,KAAK,OAAO;AAAA,EACnC;AAAA,EAEA,aAAmB;AACjB,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAM;AACd,WAAK,KAAK;AAAA,IACZ;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK,eAAgB;AACzB,SAAK,iBAAiB,WAAW,MAAM;AACrC,WAAK,iBAAiB;AACtB,WAAK,QAAQ;AAAA,IACf,GAAG,GAAI;AAAA,EACT;AACF;;;AJtGA,IAAM,YAAYC,YAAW;AAC7B,IAAM,cAAc,QAAQ,IAAI,6BAA6B,WAAW,UAAU,MAAM,GAAG,CAAC,CAAC;AAE7F,IAAM,SAAS,IAAI;AAAA,EACjB;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,cAAc;AAAA,MACZ,cAAc,EAAE,kBAAkB,CAAC,EAAE;AAAA,MACrC,OAAO,CAAC;AAAA,IACV;AAAA,IACA,cACE;AAAA,EAIJ;AACF;AAGA,IAAM,SAAS,WAAW;AAC1B,IAAM,UAAU,QAAQ,IAAI,yBAAyB,OAAO,IAAI;AAChE,IAAM,UAAU,QAAQ,IAAI,wBAAwB,SAAS,QAAQ,IAAI,uBAAuB,EAAE,IAAI,OAAO,IAAI;AACjH,IAAM,WAAW,QAAQ,IAAI,0BAA0B,OAAO,IAAI;AAGlE,IAAM,YAAY,IAAI;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIA,OAAO,kBAAkB,wBAAwB,aAAa;AAAA,EAC5D,OAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa;AAAA,QACX,MAAM;AAAA,QACN,YAAY;AAAA,UACV,OAAO,EAAE,MAAM,UAAU,aAAa,6BAA6B;AAAA,UACnE,SAAS,EAAE,MAAM,UAAU,aAAa,yBAAyB;AAAA,UACjE,OAAO;AAAA,YACL,MAAM;AAAA,YACN,MAAM,CAAC,QAAQ,WAAW,SAAS,SAAS;AAAA,YAC5C,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,UAAU,CAAC,SAAS,SAAS;AAAA,MAC/B;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa;AAAA,QACX,MAAM;AAAA,QACN,YAAY;AAAA,UACV,SAAS,EAAE,MAAM,UAAU,aAAa,8CAA8C;AAAA,QACxF;AAAA,QACA,UAAU,CAAC,SAAS;AAAA,MACtB;AAAA,IACF;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa;AAAA,QACX,MAAM;AAAA,QACN,YAAY;AAAA,UACV,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,MAAM,CAAC,QAAQ,WAAW,eAAe;AAAA,YACzC,aAAa;AAAA,UACf;AAAA,QACF;AAAA,QACA,UAAU,CAAC,QAAQ;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF,EAAE;AAEF,OAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,QAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAE1C,UAAQ,MAAM;AAAA,IACZ,KAAK,UAAU;AACb,YAAM,QAAQ,MAAM;AACpB,YAAM,UAAU,MAAM;AACtB,YAAM,QAAS,MAAM,SAAyB;AAC9C,aAAO,KAAK,WAAW,KAAK,MAAM,KAAK,MAAM,OAAO,EAAE;AACtD,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,uBAAuB,KAAK,IAAI,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,IAEA,KAAK,SAAS;AACZ,YAAM,UAAU,MAAM;AACtB,aAAO,KAAK,UAAU,QAAQ,MAAM,GAAG,GAAG,CAAC,KAAK;AAChD,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MACF,CAAC;AACD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,6BAA6B,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,IAEA,KAAK,UAAU;AACb,YAAM,SAAS,MAAM;AACrB,aAAO,KAAK,kBAAkB,MAAM,EAAE;AACtC,gBAAU,KAAK;AAAA,QACb,MAAM;AAAA,QACN;AAAA,QACA;AAAA,MACF,CAAC;AACD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,sBAAsB,MAAM,KAAK,CAAC;AAAA,MACpE;AAAA,IACF;AAAA,IAEA;AACE,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAiB,IAAI,GAAG,CAAC;AAAA,QACzD,SAAS;AAAA,MACX;AAAA,EACJ;AACF,CAAC;AAID,eAAe,OAAO;AACpB,SAAO,KAAK,yCAAyC,SAAS,GAAG;AAGjE,YAAU,QAAQ;AAGlB,YAAU,UAAU,OAAO,QAAQ;AACjC,QAAI,IAAI,SAAS,wBAAwB,IAAI,cAAc,WAAW;AACpE,aAAO,KAAK,2BAA2B,IAAI,OAAO,EAAE;AACpD,YAAM,OAAO,aAAa;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ;AAAA,UACN,SAAS,IAAI;AAAA,UACb,MAAM,EAAE,QAAQ,aAAa,WAAW,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,QAC7D;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AAED,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAC9B,SAAO,KAAK,qCAAqC;AACnD;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,SAAO,MAAM,gBAAgB,GAAG;AAChC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["randomUUID","path","config","config","sessionId","sessionName","hubHost","hubPort","randomUUID"]}
|
|
1
|
+
{"version":3,"sources":["../../src/channel/server.ts","../../src/shared/logger.ts","../../src/shared/constants.ts","../../src/shared/config.ts","../../src/channel/hub-client.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\r\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\r\nimport {\r\n CallToolRequestSchema,\r\n ListToolsRequestSchema,\r\n} from '@modelcontextprotocol/sdk/types.js';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { logger } from '../shared/logger.js';\r\nimport { CHANNEL_SERVER_NAME, CHANNEL_SERVER_VERSION } from '../shared/constants.js';\r\nimport { loadConfig } from '../shared/config.js';\r\nimport { HubClient } from './hub-client.js';\r\nimport type { SessionStatus, NotifyLevel } from '../shared/types.js';\r\n\r\nconst sessionId = randomUUID();\r\nconst sessionName = process.env.CLAUDE_ALARM_SESSION_NAME ?? `session-${sessionId.slice(0, 8)}`;\r\n\r\nconst server = new Server(\r\n {\r\n name: CHANNEL_SERVER_NAME,\r\n version: CHANNEL_SERVER_VERSION,\r\n },\r\n {\r\n capabilities: {\r\n experimental: { 'claude/channel': {} },\r\n tools: {},\r\n },\r\n instructions:\r\n 'Messages from the claude-alarm dashboard arrive as <channel source=\"claude-alarm\" sender=\"...\">. ' +\r\n 'Read the message and act on it. Reply with the same detail and depth as you normally would — do not shorten your response. ' +\r\n 'To reply, call the reply tool with the message content. ' +\r\n 'Use the notify tool to send desktop notifications. Use the status tool to update your session status.',\r\n },\r\n);\r\n\r\n// Load config for hub connection (env vars take priority)\r\nconst config = loadConfig();\r\nconst hubHost = process.env.CLAUDE_ALARM_HUB_HOST ?? config.hub.host;\r\nconst hubPort = process.env.CLAUDE_ALARM_HUB_PORT ? parseInt(process.env.CLAUDE_ALARM_HUB_PORT, 10) : config.hub.port;\r\nconst hubToken = process.env.CLAUDE_ALARM_HUB_TOKEN ?? config.hub.token;\r\n\r\n// Hub client for forwarding to central hub\r\nconst hubClient = new HubClient(\r\n sessionId,\r\n sessionName,\r\n hubHost,\r\n hubPort,\r\n hubToken,\r\n);\r\n\r\n// --- Tools ---\r\n\r\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\r\n tools: [\r\n {\r\n name: 'notify',\r\n description:\r\n 'Send a desktop notification to the user. Use this when you complete a task, encounter an error, or need user attention. The notification will appear as a system toast/popup.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n title: { type: 'string', description: 'Notification title (short)' },\r\n message: { type: 'string', description: 'Notification body text' },\r\n level: {\r\n type: 'string',\r\n enum: ['info', 'warning', 'error', 'success'],\r\n description: 'Notification level (default: info)',\r\n },\r\n },\r\n required: ['title', 'message'],\r\n },\r\n },\r\n {\r\n name: 'reply',\r\n description:\r\n 'Send a message to the web dashboard. Use this to communicate status updates, results, or any information the user should see in the monitoring dashboard.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n content: { type: 'string', description: 'Message content to display on the dashboard' },\r\n },\r\n required: ['content'],\r\n },\r\n },\r\n {\r\n name: 'status',\r\n description:\r\n 'Update your session status displayed on the dashboard. Set to \"working\" when actively processing, \"waiting_input\" when you need user input, or \"idle\" when done.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n status: {\r\n type: 'string',\r\n enum: ['idle', 'working', 'waiting_input'],\r\n description: 'Current session status',\r\n },\r\n },\r\n required: ['status'],\r\n },\r\n },\r\n ],\r\n}));\r\n\r\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\r\n const { name, arguments: args } = request.params;\r\n\r\n switch (name) {\r\n case 'notify': {\r\n const title = args?.title as string;\r\n const message = args?.message as string;\r\n const level = (args?.level as NotifyLevel) ?? 'info';\r\n logger.info(`Notify [${level}]: ${title} - ${message}`);\r\n hubClient.send({\r\n type: 'notify',\r\n sessionId,\r\n title,\r\n message,\r\n level,\r\n });\r\n return {\r\n content: [{ type: 'text', text: `Notification sent: \"${title}\"` }],\r\n };\r\n }\r\n\r\n case 'reply': {\r\n const content = args?.content as string;\r\n logger.info(`Reply: ${content.slice(0, 100)}...`);\r\n hubClient.send({\r\n type: 'reply',\r\n sessionId,\r\n content,\r\n });\r\n return {\r\n content: [{ type: 'text', text: 'Message sent to dashboard.' }],\r\n };\r\n }\r\n\r\n case 'status': {\r\n const status = args?.status as SessionStatus;\r\n logger.info(`Status update: ${status}`);\r\n hubClient.send({\r\n type: 'status',\r\n sessionId,\r\n status,\r\n });\r\n return {\r\n content: [{ type: 'text', text: `Status updated to \"${status}\".` }],\r\n };\r\n }\r\n\r\n default:\r\n return {\r\n content: [{ type: 'text', text: `Unknown tool: ${name}` }],\r\n isError: true,\r\n };\r\n }\r\n});\r\n\r\n// --- Startup ---\r\n\r\nasync function main() {\r\n logger.info(`Starting MCP channel server (session: ${sessionId})`);\r\n\r\n // Connect to hub (non-blocking, will retry)\r\n hubClient.connect();\r\n\r\n // Listen for messages from hub and forward to Claude via channel notification\r\n hubClient.onMessage(async (msg) => {\r\n if (msg.type === 'message_to_session' && msg.sessionId === sessionId) {\r\n logger.info(`Message from dashboard: ${msg.content}`);\r\n await server.notification({\r\n method: 'notifications/claude/channel',\r\n params: {\r\n content: msg.content,\r\n meta: { sender: 'dashboard', timestamp: String(Date.now()) },\r\n },\r\n });\r\n }\r\n });\r\n\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n logger.info('MCP channel server running on stdio');\r\n}\r\n\r\nmain().catch((err) => {\r\n logger.error('Fatal error:', err);\r\n process.exit(1);\r\n});\r\n","/**\r\n * Logger that writes to stderr only.\r\n * CRITICAL: In MCP channel servers, stdout is used for the stdio protocol.\r\n * Any console.log() would corrupt the protocol. Always use this logger.\r\n */\r\nexport const logger = {\r\n info(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm] ${msg}`, ...args);\r\n },\r\n warn(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm WARN] ${msg}`, ...args);\r\n },\r\n error(msg: string, ...args: unknown[]) {\r\n console.error(`[claude-alarm ERROR] ${msg}`, ...args);\r\n },\r\n debug(msg: string, ...args: unknown[]) {\r\n if (process.env.CLAUDE_ALARM_DEBUG) {\r\n console.error(`[claude-alarm DEBUG] ${msg}`, ...args);\r\n }\r\n },\r\n};\r\n","import path from 'node:path';\r\nimport os from 'node:os';\r\n\r\nexport const DEFAULT_HUB_HOST = '127.0.0.1';\r\nexport const DEFAULT_HUB_PORT = 7890;\r\n\r\nexport const CONFIG_DIR = path.join(os.homedir(), '.claude-alarm');\r\nexport const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nexport const PID_FILE = path.join(CONFIG_DIR, 'hub.pid');\r\nexport const LOG_FILE = path.join(CONFIG_DIR, 'hub.log');\r\n\r\nexport const WS_PATH_CHANNEL = '/ws/channel';\r\nexport const WS_PATH_DASHBOARD = '/ws/dashboard';\r\n\r\nexport const CHANNEL_SERVER_NAME = 'claude-alarm';\r\nexport const CHANNEL_SERVER_VERSION = '0.1.0';\r\n","import fs from 'node:fs';\r\nimport path from 'node:path';\r\nimport { randomUUID } from 'node:crypto';\r\nimport { CONFIG_DIR, CONFIG_FILE, DEFAULT_HUB_HOST, DEFAULT_HUB_PORT } from './constants.js';\r\nimport type { AppConfig } from './types.js';\r\n\r\nconst DEFAULT_CONFIG: AppConfig = {\r\n hub: {\r\n host: DEFAULT_HUB_HOST,\r\n port: DEFAULT_HUB_PORT,\r\n },\r\n notifications: {\r\n desktop: true,\r\n sound: true,\r\n },\r\n webhooks: [],\r\n};\r\n\r\nexport function ensureConfigDir(): void {\r\n if (!fs.existsSync(CONFIG_DIR)) {\r\n fs.mkdirSync(CONFIG_DIR, { recursive: true });\r\n }\r\n}\r\n\r\nexport function loadConfig(): AppConfig {\r\n ensureConfigDir();\r\n let config: AppConfig;\r\n if (!fs.existsSync(CONFIG_FILE)) {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n } else {\r\n try {\r\n const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');\r\n const parsed = JSON.parse(raw);\r\n config = { ...DEFAULT_CONFIG, ...parsed, hub: { ...DEFAULT_CONFIG.hub, ...parsed.hub } };\r\n } catch {\r\n config = { ...DEFAULT_CONFIG, hub: { ...DEFAULT_CONFIG.hub } };\r\n }\r\n }\r\n\r\n // Auto-generate token if missing\r\n if (!config.hub.token) {\r\n config.hub.token = randomUUID();\r\n saveConfig(config);\r\n }\r\n\r\n return config;\r\n}\r\n\r\n/** Get the current token, generating one if needed */\r\nexport function getOrCreateToken(): string {\r\n const config = loadConfig();\r\n return config.hub.token!;\r\n}\r\n\r\nexport function saveConfig(config: AppConfig): void {\r\n ensureConfigDir();\r\n fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), { encoding: 'utf-8', mode: 0o600 });\r\n}\r\n\r\n/**\r\n * Add claude-alarm as an MCP channel server to .mcp.json\r\n */\r\nexport function setupMcpConfig(targetDir?: string): string {\r\n const dir = targetDir ?? process.cwd();\r\n const mcpPath = path.join(dir, '.mcp.json');\r\n\r\n let mcpConfig: Record<string, any> = {};\r\n if (fs.existsSync(mcpPath)) {\r\n try {\r\n mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));\r\n } catch {\r\n mcpConfig = {};\r\n }\r\n }\r\n\r\n if (!mcpConfig.mcpServers) {\r\n mcpConfig.mcpServers = {};\r\n }\r\n\r\n mcpConfig.mcpServers['claude-alarm'] = {\r\n command: 'npx',\r\n args: ['-y', '@delt/claude-alarm', 'serve'],\r\n env: {\r\n CLAUDE_ALARM_SESSION_NAME: path.basename(dir),\r\n },\r\n };\r\n\r\n fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2), 'utf-8');\r\n return mcpPath;\r\n}\r\n","import WebSocket from 'ws';\r\nimport { logger } from '../shared/logger.js';\r\nimport { DEFAULT_HUB_HOST, DEFAULT_HUB_PORT, WS_PATH_CHANNEL } from '../shared/constants.js';\r\nimport type { ChannelMessage, SessionInfo } from '../shared/types.js';\r\n\r\nexport class HubClient {\r\n private ws: WebSocket | null = null;\r\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n private messageHandlers: Array<(msg: ChannelMessage) => void> = [];\r\n private queue: ChannelMessage[] = [];\r\n private connected = false;\r\n\r\n constructor(\r\n private sessionId: string,\r\n private sessionName: string,\r\n private hubHost = DEFAULT_HUB_HOST,\r\n private hubPort = DEFAULT_HUB_PORT,\r\n private token?: string,\r\n ) {}\r\n\r\n connect(): void {\r\n const tokenQuery = this.token ? `?token=${encodeURIComponent(this.token)}` : '';\r\n const url = `ws://${this.hubHost}:${this.hubPort}${WS_PATH_CHANNEL}${tokenQuery}`;\r\n logger.debug(`Connecting to hub at ${url}`);\r\n\r\n try {\r\n this.ws = new WebSocket(url);\r\n\r\n this.ws.on('open', () => {\r\n logger.info('Connected to hub');\r\n this.connected = true;\r\n\r\n // Register this session\r\n const registration: ChannelMessage = {\r\n type: 'register',\r\n session: {\r\n id: this.sessionId,\r\n name: this.sessionName,\r\n status: 'idle',\r\n connectedAt: Date.now(),\r\n lastActivity: Date.now(),\r\n cwd: process.cwd(),\r\n channelEnabled: true,\r\n },\r\n };\r\n this.ws!.send(JSON.stringify(registration));\r\n\r\n // Flush queued messages\r\n for (const msg of this.queue) {\r\n this.ws!.send(JSON.stringify(msg));\r\n }\r\n this.queue = [];\r\n });\r\n\r\n this.ws.on('message', (data) => {\r\n try {\r\n const msg = JSON.parse(data.toString()) as ChannelMessage;\r\n for (const handler of this.messageHandlers) {\r\n handler(msg);\r\n }\r\n } catch (err) {\r\n logger.warn('Failed to parse hub message:', err);\r\n }\r\n });\r\n\r\n this.ws.on('close', () => {\r\n logger.info('Disconnected from hub');\r\n this.connected = false;\r\n this.scheduleReconnect();\r\n });\r\n\r\n this.ws.on('error', (err) => {\r\n logger.debug(`Hub connection error: ${err.message}`);\r\n this.connected = false;\r\n });\r\n } catch {\r\n logger.debug('Failed to connect to hub, will retry');\r\n this.scheduleReconnect();\r\n }\r\n }\r\n\r\n send(msg: ChannelMessage): void {\r\n if (this.connected && this.ws?.readyState === WebSocket.OPEN) {\r\n this.ws.send(JSON.stringify(msg));\r\n } else {\r\n if (this.queue.length < 100) {\r\n this.queue.push(msg);\r\n }\r\n logger.debug('Hub not connected, message queued');\r\n }\r\n }\r\n\r\n onMessage(handler: (msg: ChannelMessage) => void): void {\r\n this.messageHandlers.push(handler);\r\n }\r\n\r\n disconnect(): void {\r\n if (this.reconnectTimer) {\r\n clearTimeout(this.reconnectTimer);\r\n this.reconnectTimer = null;\r\n }\r\n if (this.ws) {\r\n this.ws.close();\r\n this.ws = null;\r\n }\r\n this.connected = false;\r\n }\r\n\r\n private scheduleReconnect(): void {\r\n if (this.reconnectTimer) return;\r\n this.reconnectTimer = setTimeout(() => {\r\n this.reconnectTimer = null;\r\n this.connect();\r\n }, 5000);\r\n }\r\n}\r\n"],"mappings":"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","names":["randomUUID","path","config","config","sessionId","sessionName","hubHost","hubPort","randomUUID"]}
|
package/dist/cli.js
CHANGED
|
@@ -446,8 +446,12 @@ var init_server = __esm({
|
|
|
446
446
|
const candidates = [
|
|
447
447
|
path3.join(__dirname, "..", "dashboard", "index.html"),
|
|
448
448
|
// from dist/hub/
|
|
449
|
+
path3.join(__dirname, "dashboard", "index.html"),
|
|
450
|
+
// from dist/ (bundled index.js)
|
|
449
451
|
path3.join(__dirname, "..", "..", "src", "dashboard", "index.html"),
|
|
450
452
|
// from dist/hub/ -> src/
|
|
453
|
+
path3.join(__dirname, "..", "src", "dashboard", "index.html"),
|
|
454
|
+
// from dist/ -> src/
|
|
451
455
|
path3.join(process.cwd(), "dist", "dashboard", "index.html"),
|
|
452
456
|
// from cwd
|
|
453
457
|
path3.join(process.cwd(), "src", "dashboard", "index.html")
|