slackhive 0.4.8 → 0.5.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 +43 -19
- package/cli/README.md +341 -0
- package/{dist → cli/dist}/index.js +192 -132
- package/package.json +43 -33
- package/scripts/postinstall.js +26 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pelago Labs
|
|
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
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# SlackHive
|
|
7
7
|
|
|
8
8
|
### Build your AI-first company on Slack
|
|
9
|
-
#### A Karpathy-inspired team of humans and AI specialists, powered by Claude
|
|
9
|
+
#### A Karpathy-inspired team of humans and AI specialists, powered by Codex/OpenAI or Claude/Anthropic
|
|
10
10
|
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](https://www.npmjs.com/package/slackhive)
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
|
|
24
24
|
<img src="https://raw.githubusercontent.com/pelago-labs/slackhive/main/docs/images/slack-ai-agent-team-dashboard-with-avatars.png" alt="SlackHive dashboard" style="border-radius: 12px;" />
|
|
25
25
|
|
|
26
|
+
<br /><br />
|
|
27
|
+
|
|
28
|
+
<sub><b>What it looks like in Slack</b> — a SlackHive agent collaborating with the team in a real channel: daily report, follow-up questions, metric correction, and an inline PR — all in the same thread.</sub>
|
|
29
|
+
|
|
30
|
+
<img src="https://raw.githubusercontent.com/pelago-labs/slackhive/main/docs/images/slackhive-in-action-growth-analytics.png" alt="SlackHive agent collaborating in a Slack growth-analytics channel: daily growth report, follow-up questions, and an inline metric correction with a PR" style="border-radius: 12px;" />
|
|
31
|
+
|
|
26
32
|
</div>
|
|
27
33
|
|
|
28
34
|
---
|
|
@@ -86,10 +92,10 @@ slackhive init
|
|
|
86
92
|
```
|
|
87
93
|
|
|
88
94
|
The CLI will:
|
|
89
|
-
1. Check prerequisites (Git
|
|
95
|
+
1. Check prerequisites (Git and Node.js ≥ 20)
|
|
90
96
|
2. Clone the repository
|
|
91
|
-
3.
|
|
92
|
-
4.
|
|
97
|
+
3. Walk you through admin credentials and local configuration
|
|
98
|
+
4. Tell you where to connect Codex/OpenAI or Claude/Anthropic in Settings
|
|
93
99
|
5. Build and start the services natively - no Docker required
|
|
94
100
|
|
|
95
101
|
Open `http://localhost:3001` and create your first agent.
|
|
@@ -121,7 +127,10 @@ Installing Node via [nvm](https://github.com/nvm-sh/nvm) or Homebrew (`brew inst
|
|
|
121
127
|
|
|
122
128
|
### Option B: Manual setup
|
|
123
129
|
|
|
124
|
-
**Prerequisites:** Node.js ≥ 20, Git,
|
|
130
|
+
**Prerequisites:** Node.js ≥ 20, Git, and one AI backend:
|
|
131
|
+
|
|
132
|
+
- OpenAI Codex / ChatGPT via `codex login` or `OPENAI_API_KEY`
|
|
133
|
+
- Claude Code / Anthropic via `claude login` or `ANTHROPIC_API_KEY`
|
|
125
134
|
|
|
126
135
|
```bash
|
|
127
136
|
git clone https://github.com/pelago-labs/slackhive.git
|
|
@@ -132,7 +141,8 @@ cp .env.example .env
|
|
|
132
141
|
Edit `.env` with your credentials:
|
|
133
142
|
|
|
134
143
|
```env
|
|
135
|
-
|
|
144
|
+
OPENAI_API_KEY=sk-... # optional: OpenAI API billing
|
|
145
|
+
ANTHROPIC_API_KEY=sk-ant-... # optional: Anthropic API billing
|
|
136
146
|
ADMIN_USERNAME=admin
|
|
137
147
|
ADMIN_PASSWORD=<strong-random-password>
|
|
138
148
|
AUTH_SECRET= # generate: openssl rand -hex 32
|
|
@@ -150,6 +160,8 @@ npx slackhive start
|
|
|
150
160
|
|
|
151
161
|
Open `http://localhost:3001`, log in, and create your first agent.
|
|
152
162
|
|
|
163
|
+
Choose the active runtime in **Settings → AI Backend**. API keys in `.env` are available there, but the backend choice itself is managed in the dashboard.
|
|
164
|
+
|
|
153
165
|
> Full setup guide → [slackhive.mintlify.app/quickstart](https://slackhive.mintlify.app/quickstart)
|
|
154
166
|
|
|
155
167
|
---
|
|
@@ -158,7 +170,7 @@ Open `http://localhost:3001`, log in, and create your first agent.
|
|
|
158
170
|
|
|
159
171
|
### 🤖 Real AI Agents
|
|
160
172
|
|
|
161
|
-
Every agent
|
|
173
|
+
Every agent has tools, memory, identity, and instructions. When you @mention one in Slack, you're running a real AI agent that can use tools, take action, and get smarter over time. SlackHive can run agents through OpenAI Codex/ChatGPT or Claude Code/Anthropic while keeping the same Slack, MCP, memory, jobs, and observability layers.
|
|
162
174
|
|
|
163
175
|
| | |
|
|
164
176
|
|---|---|
|
|
@@ -166,8 +178,9 @@ Every agent is a full **Claude Code** agent - with tools, memory, identity, and
|
|
|
166
178
|
| 🔌 **MCP Tool Integration** | Connect any MCP server (Redshift, GitHub, Notion, Figma, custom APIs) - stdio, SSE, or HTTP transports. |
|
|
167
179
|
| 📝 **Inline TypeScript MCPs** | Paste TypeScript source directly into the UI - no deployment needed. The runner compiles and executes it. |
|
|
168
180
|
| 🧵 **Full Thread Context** | Agents fetch the entire Slack thread on every invocation - zero context lost in handoffs. |
|
|
169
|
-
| 💾 **Session Continuity** | Slack thread ↔
|
|
181
|
+
| 💾 **Session Continuity** | Slack thread ↔ agent session mapping survives restarts. Pick up exactly where you left off. |
|
|
170
182
|
| 🔐 **Encrypted Secret Store** | API keys encrypted at rest (AES-256). MCPs reference secrets by name - raw values never touch the API or UI. |
|
|
183
|
+
| 🧯 **Backup & Recovery** | Scheduled SQLite backups, manual backup/download, password-wrapped recovery key export, and CLI-only restore with pre-restore safety snapshots. |
|
|
171
184
|
| 🔁 **Hot Reload** | Edit instructions, skills, or tools and the agent picks up changes within seconds. No restart needed. |
|
|
172
185
|
|
|
173
186
|
### 👑 Boss + Specialist Hierarchy
|
|
@@ -177,20 +190,23 @@ Every agent is a full **Claude Code** agent - with tools, memory, identity, and
|
|
|
177
190
|
| 👑 **Boss Orchestration** | Boss reads your message, finds the right specialist, delegates by @mention in the same thread, and summarizes the result. |
|
|
178
191
|
| 🏢 **Multi-Boss Support** | Run multiple Boss agents for different domains (engineering, data, support). Specialists can report to more than one boss. |
|
|
179
192
|
| 📋 **Auto-Generated Registries** | Every Boss gets a live team roster auto-regenerated whenever the team changes. No manual maintenance. |
|
|
180
|
-
| 🛠 **Skills** | Markdown
|
|
181
|
-
| ⏰ **Scheduled Jobs** | Cron-based recurring tasks - daily reports, weekly digests, monitoring alerts - posted to any Slack channel or DM. |
|
|
193
|
+
| 🛠 **Skills** | Markdown playbooks that travel with an agent. Give agents SQL rules, writing guidelines, or domain workflows. |
|
|
194
|
+
| ⏰ **Scheduled Jobs** | Cron-based recurring tasks - daily reports, weekly digests, monitoring alerts - posted to any Slack channel or DM. Jobs can skip quiet notifications when nothing needs attention. |
|
|
182
195
|
|
|
183
196
|
### ⚙️ Platform
|
|
184
197
|
|
|
185
198
|
| | |
|
|
186
199
|
|---|---|
|
|
187
|
-
| 🧙 **
|
|
200
|
+
| 🧙 **Agent Launch Wizard** | Two-step setup: define identity, then choose a starting profile or blank brief. Tune prompts, skills, tools, Slack, and access after creation. |
|
|
188
201
|
| 🕓 **Version Control** | Every save auto-snapshots the full agent state. Browse history with line-level diffs, restore any version in one click. |
|
|
189
202
|
| 🔒 **Auth & RBAC** | 4 roles (superadmin / admin / editor / viewer), HMAC-signed sessions, per-agent access grants (View or Edit). Agents are hidden by default — admins grant access explicitly. No external auth provider needed. |
|
|
190
203
|
| 🚦 **Channel Restrictions** | Lock agents to specific Slack channels. Bot auto-leaves uninvited channels with a notice. |
|
|
191
204
|
| 📊 **Live Logs** | SSE-streamed log output per agent - with level filters and search. |
|
|
192
205
|
| 🧠 **Memory Viewer** | Browse, inspect, and delete agent memories by type - feedback, user, project, reference. |
|
|
193
|
-
| 📡 **Activity
|
|
206
|
+
| 📡 **Activity** | Live board of active, completed, and errored work, with initiator, agent, replay, and trace drill-down. |
|
|
207
|
+
| 🔎 **Observability** | Operational view for sessions, tokens, model usage, latency, tool reliability, sensitive access, satisfaction, and replay. |
|
|
208
|
+
| 🧪 **Agent Evals** | Static healthchecks plus approved regression cases with phrase, tool-call, and LLM-judge checks. |
|
|
209
|
+
| 🛡️ **Sensitive Data Controls** | Per-agent detection modes, Smart sensitivity checks, redaction levels, and sensitive-event audit trails. |
|
|
194
210
|
| 🌐 **Multi-Workspace** | Connect multiple Slack workspaces to a single SlackHive instance. Each workspace gets its own agents and configurations. |
|
|
195
211
|
| 📚 **Knowledge Library** | Platform-level catalog of Karpathy-style wiki folders. Owners ingest a repo / file / URL into a folder; assign one folder to many agents. The agent's `/wiki` skill auto-enables when at least one assigned folder has built content. |
|
|
196
212
|
|
|
@@ -222,7 +238,7 @@ Slack Workspace (@boss, @data-bot, @writer, ...)
|
|
|
222
238
|
**How a message flows:**
|
|
223
239
|
1. User @mentions an agent in Slack
|
|
224
240
|
2. Runner receives the event via Bolt Socket Mode
|
|
225
|
-
3.
|
|
241
|
+
3. The selected AI backend processes the message with the agent's instructions and skills
|
|
226
242
|
4. Agent uses MCP tools if needed (Redshift, GitHub, Notion, etc.)
|
|
227
243
|
5. Response is formatted as Slack Block Kit and posted to the thread
|
|
228
244
|
6. Memory files written during the session are synced to SQLite
|
|
@@ -230,17 +246,24 @@ Slack Workspace (@boss, @data-bot, @writer, ...)
|
|
|
230
246
|
|
|
231
247
|
---
|
|
232
248
|
|
|
233
|
-
## 🔑
|
|
249
|
+
## 🔑 AI Backend Authentication
|
|
250
|
+
|
|
251
|
+
Use one backend to start; teams can migrate later without recreating agents.
|
|
252
|
+
|
|
253
|
+
**OpenAI Codex / ChatGPT**
|
|
254
|
+
```env
|
|
255
|
+
OPENAI_API_KEY=sk-...
|
|
256
|
+
```
|
|
234
257
|
|
|
235
|
-
|
|
258
|
+
Then choose **OpenAI Codex** in **Settings → AI Backend**. You can also use a local Codex login when available on your machine.
|
|
236
259
|
|
|
237
|
-
**
|
|
260
|
+
**Claude Code / Anthropic**
|
|
238
261
|
```env
|
|
239
262
|
ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
240
263
|
```
|
|
241
264
|
Billed per token via the Anthropic API. Best for teams and production.
|
|
242
265
|
|
|
243
|
-
**
|
|
266
|
+
Then choose **Claude Code** in **Settings → AI Backend**. You can also use Claude Pro or Max subscription auth:
|
|
244
267
|
|
|
245
268
|
First, install Claude Code on your host machine:
|
|
246
269
|
|
|
@@ -277,9 +300,10 @@ Leave `ANTHROPIC_API_KEY` unset - the runner picks up credentials from the syste
|
|
|
277
300
|
- [x] Encrypted environment variables
|
|
278
301
|
- [x] Channel restrictions
|
|
279
302
|
- [x] Multi-workspace support
|
|
303
|
+
- [x] Multi-backend AI support
|
|
304
|
+
- [x] Observability, traces, replay, tokens, and sensitive-access review
|
|
280
305
|
- [ ] Webhook triggers (GitHub, Jira, PagerDuty → agent actions)
|
|
281
306
|
- [ ] Agent-to-agent direct messaging
|
|
282
|
-
- [ ] Analytics dashboard
|
|
283
307
|
- [ ] Custom tool builder (no MCP server needed)
|
|
284
308
|
- [ ] Agent templates marketplace
|
|
285
309
|
- [ ] RAG integration - connect agents to document stores
|
|
@@ -337,5 +361,5 @@ Use [GitHub's private vulnerability reporting](https://github.com/pelago-labs/sl
|
|
|
337
361
|
MIT © 2026 [Pelago Labs](https://github.com/pelago-labs)
|
|
338
362
|
|
|
339
363
|
<div align="center">
|
|
340
|
-
<sub>Built with
|
|
364
|
+
<sub>Built with AI backends, Slack Bolt, and a lot of coffee</sub>
|
|
341
365
|
</div>
|
package/cli/README.md
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/pelago-labs/slackhive/main/apps/web/public/logo.svg" alt="SlackHive" width="80" />
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# SlackHive
|
|
7
|
+
|
|
8
|
+
### Build your AI-first company on Slack
|
|
9
|
+
#### A Karpathy-inspired team of humans and AI specialists, powered by Claude Code
|
|
10
|
+
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://www.npmjs.com/package/slackhive)
|
|
13
|
+
[](https://www.npmjs.com/package/slackhive)
|
|
14
|
+
[](https://nodejs.org)
|
|
15
|
+
[](https://www.typescriptlang.org)
|
|
16
|
+
[](https://sqlite.org)
|
|
17
|
+
[](https://slackhive.mintlify.app)
|
|
18
|
+
[](https://github.com/pelago-labs/slackhive/actions/workflows/audit.yml)
|
|
19
|
+
|
|
20
|
+
[**Documentation**](https://slackhive.mintlify.app) · [Quick Start](#-quick-start) · [Features](#-features) · [Architecture](#-architecture) · [Contributing](#-contributing)
|
|
21
|
+
|
|
22
|
+
<br />
|
|
23
|
+
|
|
24
|
+
<img src="https://raw.githubusercontent.com/pelago-labs/slackhive/main/docs/images/slack-ai-agent-team-dashboard-with-avatars.png" alt="SlackHive dashboard" style="border-radius: 12px;" />
|
|
25
|
+
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Why SlackHive?
|
|
31
|
+
|
|
32
|
+
> **A workspace where humans and AI agents work side by side beats any team made of just one or the other.**
|
|
33
|
+
|
|
34
|
+
That's the whole bet. Not "AI is going to replace your team." A genuinely hybrid team — your people plus a roster of role-specific AI agents — collaborating in the same Slack channels, on the same threads, with the same `@mention` your team already uses for each other.
|
|
35
|
+
|
|
36
|
+
You don't switch to a separate AI app. You don't open a new tab. You `@data-analyst` in `#sales` the way you'd ping a real one. The agent reads the full thread, queries your warehouse, and posts the answer 40 seconds later — while another channel is `@designer`-ing mockups in Figma and your `@devops` agent is quietly opening a PR for the 500s the engineer just complained about.
|
|
37
|
+
|
|
38
|
+
Why this beats the alternatives:
|
|
39
|
+
- **Vs. an all-human team** — slower, more context-switching, "let me get back to you" everywhere
|
|
40
|
+
- **Vs. one mega-AI assistant** (single ChatGPT-style entry point) — no domain expertise, no team patterns, lives outside your workflow
|
|
41
|
+
- **Vs. AI tools with their own siloed UI** — separate interface = friction = nobody uses it after week two
|
|
42
|
+
|
|
43
|
+
**Anyone on the team can create an agent.** No engineers, no platform team — if you can describe what you need, you can deploy it in minutes.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
CEO: @data-analyst revenue is down 8% this week, can you dig in?
|
|
47
|
+
DataBot: [queries Redshift across 6 dimensions]
|
|
48
|
+
Found it - enterprise churn spiked Tuesday after the pricing change.
|
|
49
|
+
3 accounts, $42k ARR at risk.
|
|
50
|
+
|
|
51
|
+
Engineer: @devops the checkout service is throwing 500s
|
|
52
|
+
DevOps: [reads logs, identifies root cause, opens PR]
|
|
53
|
+
Memory leak in the payment processor pool. PR #847 is up with the fix.
|
|
54
|
+
|
|
55
|
+
PM: @designer mock up a simpler onboarding flow
|
|
56
|
+
Designer: [creates Figma frames via MCP]
|
|
57
|
+
Done - 3 variants in Figma. Which direction do you want to take?
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Tag a specialist directly when you know who to ask. Or tag `@boss` when you're not sure - Boss finds the right specialist, delegates, and summarizes the result:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
You: @boss can you analyze last week's conversion funnel?
|
|
64
|
+
Boss: That's right up @data-analyst's alley 👇
|
|
65
|
+
@data-analyst - conversion funnel analysis for last week.
|
|
66
|
+
When you're done, please tag @boss.
|
|
67
|
+
DataBot: Conversions up 12% WoW, checkout completion jumped 3×. @boss - done!
|
|
68
|
+
Boss: Conversions are up 12% WoW. The win was checkout - 3× completion rate.
|
|
69
|
+
Want me to pull a channel or cohort breakdown?
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> **Under the hood:** small specialist agents instead of one mega-agent (Karpathy: *specialists beat generalists*), each grounded in a per-folder Karpathy-style wiki (LLMs read structured wikis better than raw file dumps), with a Boss agent that delegates so no single context window has to hold the whole company. The architecture is a means; the team-in-Slack experience is the point.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 🚀 Quick Start
|
|
77
|
+
|
|
78
|
+
### Option A: One-command install (recommended)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm install -g slackhive
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
slackhive init
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The CLI will:
|
|
89
|
+
1. Check prerequisites (Git, Node.js ≥ 20, Claude Code)
|
|
90
|
+
2. Clone the repository
|
|
91
|
+
3. Auto-detect your Claude installation (cross-platform compatible)
|
|
92
|
+
4. Walk you through configuration (API key or subscription, admin credentials)
|
|
93
|
+
5. Build and start the services natively - no Docker required
|
|
94
|
+
|
|
95
|
+
Open `http://localhost:3001` and create your first agent.
|
|
96
|
+
|
|
97
|
+
#### Hit `EACCES: permission denied` on install?
|
|
98
|
+
|
|
99
|
+
If you installed Node from nodejs.org on macOS/Linux, `/usr/local/lib/node_modules` is root-owned and `npm install -g` fails without sudo. Point npm at a user-owned prefix once - no sudo needed again:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
mkdir -p ~/.npm-global
|
|
103
|
+
npm config set prefix '~/.npm-global'
|
|
104
|
+
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.zshrc # or ~/.bashrc
|
|
105
|
+
source ~/.zshrc
|
|
106
|
+
npm install -g slackhive
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Installing Node via [nvm](https://github.com/nvm-sh/nvm) or Homebrew (`brew install node`) puts it in a user-writable location by default and avoids this entirely. If you'd rather skip global install, `npx slackhive init` works too - just prefix every command with `npx`.
|
|
110
|
+
|
|
111
|
+
### CLI Commands
|
|
112
|
+
|
|
113
|
+
| Command | Description |
|
|
114
|
+
|---------|-------------|
|
|
115
|
+
| `slackhive init` | Clone, configure, and start SlackHive |
|
|
116
|
+
| `slackhive start` | Start all services |
|
|
117
|
+
| `slackhive stop` | Stop all services (and any orphaned runner processes) |
|
|
118
|
+
| `slackhive status` | Show running services + ports |
|
|
119
|
+
| `slackhive logs` | Tail runner logs |
|
|
120
|
+
| `slackhive update` | Pull latest changes and rebuild |
|
|
121
|
+
|
|
122
|
+
### Option B: Manual setup
|
|
123
|
+
|
|
124
|
+
**Prerequisites:** Node.js ≥ 20, Git, [Claude Code](https://docs.anthropic.com/en/docs/claude-code/setup) on your PATH
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git clone https://github.com/pelago-labs/slackhive.git
|
|
128
|
+
cd slackhive
|
|
129
|
+
cp .env.example .env
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Edit `.env` with your credentials:
|
|
133
|
+
|
|
134
|
+
```env
|
|
135
|
+
ANTHROPIC_API_KEY=sk-ant-... # or use Claude Pro/Max subscription
|
|
136
|
+
ADMIN_USERNAME=admin
|
|
137
|
+
ADMIN_PASSWORD=<strong-random-password>
|
|
138
|
+
AUTH_SECRET= # generate: openssl rand -hex 32
|
|
139
|
+
ENV_SECRET_KEY= # generate: openssl rand -hex 32
|
|
140
|
+
DATABASE_TYPE=sqlite # embedded - no external DB needed
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npm install
|
|
145
|
+
npm run build
|
|
146
|
+
|
|
147
|
+
# Start the stack (web on :3001, runner on :3002)
|
|
148
|
+
npx slackhive start
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Open `http://localhost:3001`, log in, and create your first agent.
|
|
152
|
+
|
|
153
|
+
> Full setup guide → [slackhive.mintlify.app/quickstart](https://slackhive.mintlify.app/quickstart)
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## ✨ Features
|
|
158
|
+
|
|
159
|
+
### 🤖 Real AI Agents
|
|
160
|
+
|
|
161
|
+
Every agent is a full **Claude Code** agent - with tools, memory, identity, and instructions. When you @mention one in Slack, you're running a real AI agent that can use tools, take action, and get smarter over time.
|
|
162
|
+
|
|
163
|
+
| | |
|
|
164
|
+
|---|---|
|
|
165
|
+
| 🧠 **Persistent Memory** | Agents write memories during conversations - feedback, user context, project state. Synced to SQLite, injected on next start. They don't forget. |
|
|
166
|
+
| 🔌 **MCP Tool Integration** | Connect any MCP server (Redshift, GitHub, Notion, Figma, custom APIs) - stdio, SSE, or HTTP transports. |
|
|
167
|
+
| 📝 **Inline TypeScript MCPs** | Paste TypeScript source directly into the UI - no deployment needed. The runner compiles and executes it. |
|
|
168
|
+
| 🧵 **Full Thread Context** | Agents fetch the entire Slack thread on every invocation - zero context lost in handoffs. |
|
|
169
|
+
| 💾 **Session Continuity** | Slack thread ↔ Claude session mapping survives restarts. Pick up exactly where you left off. |
|
|
170
|
+
| 🔐 **Encrypted Secret Store** | API keys encrypted at rest (AES-256). MCPs reference secrets by name - raw values never touch the API or UI. |
|
|
171
|
+
| 🔁 **Hot Reload** | Edit instructions, skills, or tools and the agent picks up changes within seconds. No restart needed. |
|
|
172
|
+
|
|
173
|
+
### 👑 Boss + Specialist Hierarchy
|
|
174
|
+
|
|
175
|
+
| | |
|
|
176
|
+
|---|---|
|
|
177
|
+
| 👑 **Boss Orchestration** | Boss reads your message, finds the right specialist, delegates by @mention in the same thread, and summarizes the result. |
|
|
178
|
+
| 🏢 **Multi-Boss Support** | Run multiple Boss agents for different domains (engineering, data, support). Specialists can report to more than one boss. |
|
|
179
|
+
| 📋 **Auto-Generated Registries** | Every Boss gets a live team roster auto-regenerated whenever the team changes. No manual maintenance. |
|
|
180
|
+
| 🛠 **Skills** | Markdown files deployed as Claude Code slash commands. Give agents SQL rules, writing guidelines, or domain playbooks. |
|
|
181
|
+
| ⏰ **Scheduled Jobs** | Cron-based recurring tasks - daily reports, weekly digests, monitoring alerts - posted to any Slack channel or DM. |
|
|
182
|
+
|
|
183
|
+
### ⚙️ Platform
|
|
184
|
+
|
|
185
|
+
| | |
|
|
186
|
+
|---|---|
|
|
187
|
+
| 🧙 **Onboarding Wizard** | 5-step guided setup: identity → Slack app → credentials → tools & skills → review. |
|
|
188
|
+
| 🕓 **Version Control** | Every save auto-snapshots the full agent state. Browse history with line-level diffs, restore any version in one click. |
|
|
189
|
+
| 🔒 **Auth & RBAC** | 4 roles (superadmin / admin / editor / viewer), HMAC-signed sessions, per-agent access grants (View or Edit). Agents are hidden by default — admins grant access explicitly. No external auth provider needed. |
|
|
190
|
+
| 🚦 **Channel Restrictions** | Lock agents to specific Slack channels. Bot auto-leaves uninvited channels with a notice. |
|
|
191
|
+
| 📊 **Live Logs** | SSE-streamed log output per agent - with level filters and search. |
|
|
192
|
+
| 🧠 **Memory Viewer** | Browse, inspect, and delete agent memories by type - feedback, user, project, reference. |
|
|
193
|
+
| 📡 **Activity Dashboard** | Live kanban of every task your agents are working on - Active, Completed, Errors - with drill-down to every tool call. A superadmin-only **Usage** tab breaks down token consumption per agent and ranks power users by tasks started. Admins see everything; editors see only agents they own or are granted access to; viewers are blocked. |
|
|
194
|
+
| 🌐 **Multi-Workspace** | Connect multiple Slack workspaces to a single SlackHive instance. Each workspace gets its own agents and configurations. |
|
|
195
|
+
| 📚 **Knowledge Library** | Platform-level catalog of Karpathy-style wiki folders. Owners ingest a repo / file / URL into a folder; assign one folder to many agents. The agent's `/wiki` skill auto-enables when at least one assigned folder has built content. |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 🏗 Architecture
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
Slack Workspace (@boss, @data-bot, @writer, ...)
|
|
203
|
+
│ Socket Mode (Bolt)
|
|
204
|
+
▼
|
|
205
|
+
┌──────────────────────────────────────────────────┐
|
|
206
|
+
│ Local host - two Node.js processes │
|
|
207
|
+
│ │
|
|
208
|
+
│ Web (Next.js :3001) ──HTTP──► Runner (:3002) │
|
|
209
|
+
│ │ │ │
|
|
210
|
+
│ └───── SQLite file ──────┘ │
|
|
211
|
+
│ (./data/slackhive.db) │
|
|
212
|
+
└──────────────────────────────────────────────────┘
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
| Service | Description |
|
|
216
|
+
|---------|-------------|
|
|
217
|
+
| **Web** (Next.js 15) | Dashboard - create agents, edit skills, view logs, manage users |
|
|
218
|
+
| **Runner** (Node.js) | Hosts all agent processes and Slack connections |
|
|
219
|
+
| **SQLite** | Embedded - stores agents, memories, skills, sessions, users, history. No external DB to install |
|
|
220
|
+
| **Event bus** | Web posts to the runner's internal HTTP port to deliver hot-reload events |
|
|
221
|
+
|
|
222
|
+
**How a message flows:**
|
|
223
|
+
1. User @mentions an agent in Slack
|
|
224
|
+
2. Runner receives the event via Bolt Socket Mode
|
|
225
|
+
3. Claude Code processes the message with the agent's compiled `CLAUDE.md`
|
|
226
|
+
4. Agent uses MCP tools if needed (Redshift, GitHub, Notion, etc.)
|
|
227
|
+
5. Response is formatted as Slack Block Kit and posted to the thread
|
|
228
|
+
6. Memory files written during the session are synced to SQLite
|
|
229
|
+
7. Next conversation starts with all accumulated knowledge
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 🔑 Claude Code Authentication
|
|
234
|
+
|
|
235
|
+
Two options - use whichever fits your setup:
|
|
236
|
+
|
|
237
|
+
**Option A - API Key**
|
|
238
|
+
```env
|
|
239
|
+
ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
240
|
+
```
|
|
241
|
+
Billed per token via the Anthropic API. Best for teams and production.
|
|
242
|
+
|
|
243
|
+
**Option B - Claude Pro or Max Subscription**
|
|
244
|
+
|
|
245
|
+
First, install Claude Code on your host machine:
|
|
246
|
+
|
|
247
|
+
> **Note:** Do not install via `npm` - the npm package is deprecated and incompatible with SlackHive.
|
|
248
|
+
|
|
249
|
+
MacOS/Linux (Recommended):
|
|
250
|
+
```bash
|
|
251
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Homebrew (macOS/Linux):
|
|
255
|
+
```bash
|
|
256
|
+
brew install --cask claude-code
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
For more installation options, uninstall steps, and troubleshooting, see the [setup documentation](https://docs.anthropic.com/en/docs/claude-code/setup).
|
|
260
|
+
|
|
261
|
+
Then log in:
|
|
262
|
+
```bash
|
|
263
|
+
claude login # saves credentials to your system keychain / ~/.claude/
|
|
264
|
+
```
|
|
265
|
+
Leave `ANTHROPIC_API_KEY` unset - the runner picks up credentials from the system keychain automatically. Best for individual developers.
|
|
266
|
+
|
|
267
|
+
> Full guide → [slackhive.mintlify.app/configuration/env-vars](https://slackhive.mintlify.app/configuration/env-vars)
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 🔮 Roadmap
|
|
272
|
+
|
|
273
|
+
- [x] Boss orchestration + auto-generated team registries
|
|
274
|
+
- [x] Persistent memory system
|
|
275
|
+
- [x] Scheduled jobs
|
|
276
|
+
- [x] Version control with diff view
|
|
277
|
+
- [x] Encrypted environment variables
|
|
278
|
+
- [x] Channel restrictions
|
|
279
|
+
- [x] Multi-workspace support
|
|
280
|
+
- [ ] Webhook triggers (GitHub, Jira, PagerDuty → agent actions)
|
|
281
|
+
- [ ] Agent-to-agent direct messaging
|
|
282
|
+
- [ ] Analytics dashboard
|
|
283
|
+
- [ ] Custom tool builder (no MCP server needed)
|
|
284
|
+
- [ ] Agent templates marketplace
|
|
285
|
+
- [ ] RAG integration - connect agents to document stores
|
|
286
|
+
|
|
287
|
+
Have an idea? [Open an issue](https://github.com/pelago-labs/slackhive/issues)
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 🤝 Contributing
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
git clone https://github.com/pelago-labs/slackhive.git
|
|
295
|
+
cd slackhive && npm install
|
|
296
|
+
|
|
297
|
+
# Configure
|
|
298
|
+
cp .env.example .env # then fill in ADMIN_PASSWORD, AUTH_SECRET, ENV_SECRET_KEY
|
|
299
|
+
|
|
300
|
+
# Run locally (SQLite - no external services required)
|
|
301
|
+
cd apps/web && npm run dev # http://localhost:3000
|
|
302
|
+
cd apps/runner && npm run dev # http://localhost:3002
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Open an issue before submitting large PRs so we can align on the approach.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 👥 Contributors
|
|
310
|
+
|
|
311
|
+
<a href="https://github.com/pelago-labs/slackhive/graphs/contributors">
|
|
312
|
+
<img src="https://contrib.rocks/image?repo=pelago-labs/slackhive" alt="Contributors" />
|
|
313
|
+
</a>
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## ⭐ Star History
|
|
318
|
+
|
|
319
|
+
<a href="https://star-history.com/#pelago-labs/slackhive&Date">
|
|
320
|
+
<picture>
|
|
321
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=pelago-labs/slackhive&type=Date&theme=dark" />
|
|
322
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=pelago-labs/slackhive&type=Date" />
|
|
323
|
+
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=pelago-labs/slackhive&type=Date" width="600" />
|
|
324
|
+
</picture>
|
|
325
|
+
</a>
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 🔒 Security
|
|
330
|
+
|
|
331
|
+
Use [GitHub's private vulnerability reporting](https://github.com/pelago-labs/slackhive/security/advisories/new) - click **"Report a vulnerability"** on the Security tab. Please don't open public issues for security bugs. We respond within 48 hours.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## 📄 License
|
|
336
|
+
|
|
337
|
+
MIT © 2026 [Pelago Labs](https://github.com/pelago-labs)
|
|
338
|
+
|
|
339
|
+
<div align="center">
|
|
340
|
+
<sub>Built with Claude Code, Slack Bolt, and a lot of ☕</sub>
|
|
341
|
+
</div>
|
|
@@ -6,11 +6,20 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __esm = (fn, res) => function __init() {
|
|
10
|
-
|
|
9
|
+
var __esm = (fn, res, err) => function __init() {
|
|
10
|
+
if (err) throw err[0];
|
|
11
|
+
try {
|
|
12
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
13
|
+
} catch (e) {
|
|
14
|
+
throw err = [e], e;
|
|
15
|
+
}
|
|
11
16
|
};
|
|
12
17
|
var __commonJS = (cb, mod) => function __require() {
|
|
13
|
-
|
|
18
|
+
try {
|
|
19
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
20
|
+
} catch (e) {
|
|
21
|
+
throw mod = 0, e;
|
|
22
|
+
}
|
|
14
23
|
};
|
|
15
24
|
var __export = (target, all) => {
|
|
16
25
|
for (var name in all)
|
|
@@ -15210,7 +15219,9 @@ var require_prompts3 = __commonJS({
|
|
|
15210
15219
|
// src/commands/manage.ts
|
|
15211
15220
|
var manage_exports = {};
|
|
15212
15221
|
__export(manage_exports, {
|
|
15222
|
+
backup: () => backup,
|
|
15213
15223
|
logs: () => logs,
|
|
15224
|
+
restore: () => restore,
|
|
15214
15225
|
start: () => start,
|
|
15215
15226
|
status: () => status,
|
|
15216
15227
|
stop: () => stop,
|
|
@@ -15561,13 +15572,178 @@ async function logs(opts) {
|
|
|
15561
15572
|
async function update() {
|
|
15562
15573
|
nativeUpdate(findProjectDir());
|
|
15563
15574
|
}
|
|
15564
|
-
|
|
15575
|
+
function stamp() {
|
|
15576
|
+
const d = /* @__PURE__ */ new Date();
|
|
15577
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
15578
|
+
return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}-${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}`;
|
|
15579
|
+
}
|
|
15580
|
+
function unwrapRecoveryKey(blob, password) {
|
|
15581
|
+
if (!blob || blob.v !== 1 || blob.kdf !== "scrypt") throw new Error("Unrecognized recovery-key file format.");
|
|
15582
|
+
const sc = blob.scrypt ?? { N: 1 << 15, r: 8, p: 1 };
|
|
15583
|
+
const key = (0, import_crypto.scryptSync)(password, Buffer.from(blob.salt, "base64"), 32, { N: sc.N, r: sc.r, p: sc.p, maxmem: 64 * 1024 * 1024 });
|
|
15584
|
+
const decipher = (0, import_crypto.createDecipheriv)("aes-256-gcm", key, Buffer.from(blob.iv, "base64"));
|
|
15585
|
+
decipher.setAuthTag(Buffer.from(blob.tag, "base64"));
|
|
15586
|
+
try {
|
|
15587
|
+
return Buffer.concat([decipher.update(Buffer.from(blob.ct, "base64")), decipher.final()]).toString("utf8");
|
|
15588
|
+
} catch {
|
|
15589
|
+
throw new Error("Wrong password or corrupted recovery-key file.");
|
|
15590
|
+
}
|
|
15591
|
+
}
|
|
15592
|
+
function promptHidden(label) {
|
|
15593
|
+
return new Promise((resolve2, reject) => {
|
|
15594
|
+
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
15595
|
+
process.stdout.write(label);
|
|
15596
|
+
rl._writeToOutput = () => {
|
|
15597
|
+
};
|
|
15598
|
+
rl.on("SIGINT", () => {
|
|
15599
|
+
rl.close();
|
|
15600
|
+
process.stdout.write("\n");
|
|
15601
|
+
reject(new Error("cancelled"));
|
|
15602
|
+
});
|
|
15603
|
+
rl.question("", (answer) => {
|
|
15604
|
+
rl.close();
|
|
15605
|
+
process.stdout.write("\n");
|
|
15606
|
+
resolve2(answer);
|
|
15607
|
+
});
|
|
15608
|
+
});
|
|
15609
|
+
}
|
|
15610
|
+
function writeEnvSecretKey(projectDir, key) {
|
|
15611
|
+
const KEY_RE = /^\s*(?:export\s+)?ENV_SECRET_KEY=/;
|
|
15612
|
+
const setKeyIn = (rel, createIfMissing) => {
|
|
15613
|
+
const file = (0, import_path.join)(projectDir, rel);
|
|
15614
|
+
if (!(0, import_fs.existsSync)(file)) {
|
|
15615
|
+
if (!createIfMissing) return;
|
|
15616
|
+
(0, import_fs.writeFileSync)(file, `ENV_SECRET_KEY=${key}
|
|
15617
|
+
`);
|
|
15618
|
+
(0, import_fs.chmodSync)(file, 384);
|
|
15619
|
+
console.log(import_chalk.default.gray(` created ${rel} with ENV_SECRET_KEY`));
|
|
15620
|
+
return;
|
|
15621
|
+
}
|
|
15622
|
+
const lines = (0, import_fs.readFileSync)(file, "utf-8").split("\n");
|
|
15623
|
+
let found = false;
|
|
15624
|
+
const next = lines.map((l) => KEY_RE.test(l) ? (found = true, `ENV_SECRET_KEY=${key}`) : l);
|
|
15625
|
+
if (!found) next.push(`ENV_SECRET_KEY=${key}`);
|
|
15626
|
+
(0, import_fs.writeFileSync)(file, next.join("\n"));
|
|
15627
|
+
(0, import_fs.chmodSync)(file, 384);
|
|
15628
|
+
console.log(import_chalk.default.gray(` updated ENV_SECRET_KEY in ${rel}`));
|
|
15629
|
+
};
|
|
15630
|
+
setKeyIn(".env", true);
|
|
15631
|
+
setKeyIn("apps/web/.env", false);
|
|
15632
|
+
setKeyIn("apps/runner/.env", false);
|
|
15633
|
+
}
|
|
15634
|
+
function postBackupNow(internalPort) {
|
|
15635
|
+
return new Promise((resolve2, reject) => {
|
|
15636
|
+
const req = (0, import_http.request)({ host: "127.0.0.1", port: internalPort, path: "/backup-now", method: "POST" }, (res) => {
|
|
15637
|
+
let body = "";
|
|
15638
|
+
res.on("data", (c) => body += c);
|
|
15639
|
+
res.on("end", () => {
|
|
15640
|
+
try {
|
|
15641
|
+
resolve2(JSON.parse(body || "{}"));
|
|
15642
|
+
} catch {
|
|
15643
|
+
reject(new Error("bad runner response"));
|
|
15644
|
+
}
|
|
15645
|
+
});
|
|
15646
|
+
});
|
|
15647
|
+
req.on("error", reject);
|
|
15648
|
+
req.end();
|
|
15649
|
+
});
|
|
15650
|
+
}
|
|
15651
|
+
async function backup(options) {
|
|
15652
|
+
const dataDb = (0, import_path.join)(getSlackhiveDir(), "data.db");
|
|
15653
|
+
if (!(0, import_fs.existsSync)(dataDb)) {
|
|
15654
|
+
console.log(import_chalk.default.red(" Database not found at " + dataDb));
|
|
15655
|
+
process.exit(1);
|
|
15656
|
+
}
|
|
15657
|
+
const info = readPidInfo();
|
|
15658
|
+
const spinner = (0, import_ora.default)("Creating database backup...").start();
|
|
15659
|
+
try {
|
|
15660
|
+
if (info) {
|
|
15661
|
+
const r = await postBackupNow(info.internalPort);
|
|
15662
|
+
if (r.error) throw new Error(r.error);
|
|
15663
|
+
spinner.succeed(`Backup created: ${r.path}`);
|
|
15664
|
+
} else {
|
|
15665
|
+
const dir = options.output ? (0, import_path.dirname)(options.output) : (0, import_path.join)(getSlackhiveDir(), "backups");
|
|
15666
|
+
(0, import_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
15667
|
+
const dest = options.output ?? (0, import_path.join)(dir, `data-${stamp()}.db`);
|
|
15668
|
+
(0, import_fs.copyFileSync)(dataDb, dest);
|
|
15669
|
+
(0, import_fs.chmodSync)(dest, 384);
|
|
15670
|
+
spinner.succeed(`Backup created: ${dest} (${((0, import_fs.statSync)(dest).size / 1024 / 1024).toFixed(1)} MB)`);
|
|
15671
|
+
}
|
|
15672
|
+
} catch (err) {
|
|
15673
|
+
spinner.fail(`Backup failed: ${err.message}`);
|
|
15674
|
+
process.exit(1);
|
|
15675
|
+
}
|
|
15676
|
+
}
|
|
15677
|
+
async function restore(options) {
|
|
15678
|
+
if (!(0, import_fs.existsSync)(options.from)) {
|
|
15679
|
+
console.log(import_chalk.default.red(` Backup file not found: ${options.from}`));
|
|
15680
|
+
process.exit(1);
|
|
15681
|
+
}
|
|
15682
|
+
if (options.recoveryKey && !(0, import_fs.existsSync)(options.recoveryKey)) {
|
|
15683
|
+
console.log(import_chalk.default.red(` Recovery-key file not found: ${options.recoveryKey}`));
|
|
15684
|
+
process.exit(1);
|
|
15685
|
+
}
|
|
15686
|
+
if (options.env && !(0, import_fs.existsSync)(options.env)) {
|
|
15687
|
+
console.log(import_chalk.default.red(` Env file not found: ${options.env}`));
|
|
15688
|
+
process.exit(1);
|
|
15689
|
+
}
|
|
15690
|
+
let encryptionKey = null;
|
|
15691
|
+
if (options.recoveryKey) {
|
|
15692
|
+
let blob;
|
|
15693
|
+
try {
|
|
15694
|
+
blob = JSON.parse((0, import_fs.readFileSync)(options.recoveryKey, "utf-8"));
|
|
15695
|
+
} catch {
|
|
15696
|
+
console.log(import_chalk.default.red(" Recovery-key file is not valid JSON"));
|
|
15697
|
+
process.exit(1);
|
|
15698
|
+
}
|
|
15699
|
+
const pw = process.env.SLACKHIVE_RECOVERY_PASSWORD ?? await promptHidden(" Recovery-key password: ");
|
|
15700
|
+
try {
|
|
15701
|
+
encryptionKey = unwrapRecoveryKey(blob, pw);
|
|
15702
|
+
} catch (err) {
|
|
15703
|
+
console.log(import_chalk.default.red(` ${err.message}`));
|
|
15704
|
+
process.exit(1);
|
|
15705
|
+
}
|
|
15706
|
+
console.log(import_chalk.default.green(" Recovery key unwrapped"));
|
|
15707
|
+
}
|
|
15708
|
+
if (readPidInfo()) nativeStop();
|
|
15709
|
+
const dir = getSlackhiveDir();
|
|
15710
|
+
const dataDb = (0, import_path.join)(dir, "data.db");
|
|
15711
|
+
if ((0, import_fs.existsSync)(dataDb)) {
|
|
15712
|
+
const pre = (0, import_path.join)(dir, "backups", `pre-restore-${stamp()}.db`);
|
|
15713
|
+
(0, import_fs.mkdirSync)((0, import_path.join)(dir, "backups"), { recursive: true, mode: 448 });
|
|
15714
|
+
(0, import_fs.copyFileSync)(dataDb, pre);
|
|
15715
|
+
(0, import_fs.chmodSync)(pre, 384);
|
|
15716
|
+
console.log(import_chalk.default.gray(` current DB snapshotted \u2192 ${pre}`));
|
|
15717
|
+
}
|
|
15718
|
+
const projectDir = findProjectDir();
|
|
15719
|
+
if (encryptionKey) writeEnvSecretKey(projectDir, encryptionKey);
|
|
15720
|
+
else if (options.env) for (const rel of [".env", "apps/web/.env", "apps/runner/.env"]) {
|
|
15721
|
+
const file = (0, import_path.join)(projectDir, rel);
|
|
15722
|
+
(0, import_fs.copyFileSync)(options.env, file);
|
|
15723
|
+
(0, import_fs.chmodSync)(file, 384);
|
|
15724
|
+
console.log(import_chalk.default.gray(` copied env \u2192 ${rel}`));
|
|
15725
|
+
}
|
|
15726
|
+
(0, import_fs.copyFileSync)(options.from, dataDb);
|
|
15727
|
+
(0, import_fs.chmodSync)(dataDb, 384);
|
|
15728
|
+
for (const ext of ["-wal", "-shm"]) {
|
|
15729
|
+
try {
|
|
15730
|
+
(0, import_fs.unlinkSync)(dataDb + ext);
|
|
15731
|
+
} catch {
|
|
15732
|
+
}
|
|
15733
|
+
}
|
|
15734
|
+
console.log(import_chalk.default.green(` Database restored from ${options.from}`));
|
|
15735
|
+
console.log(import_chalk.default.gray(" Run `slackhive start` to boot the restored database."));
|
|
15736
|
+
}
|
|
15737
|
+
var import_child_process, import_fs, import_path, import_http, import_readline, import_crypto, import_chalk, import_ora;
|
|
15565
15738
|
var init_manage = __esm({
|
|
15566
15739
|
"src/commands/manage.ts"() {
|
|
15567
15740
|
"use strict";
|
|
15568
15741
|
import_child_process = require("child_process");
|
|
15569
15742
|
import_fs = require("fs");
|
|
15570
15743
|
import_path = require("path");
|
|
15744
|
+
import_http = require("http");
|
|
15745
|
+
import_readline = require("readline");
|
|
15746
|
+
import_crypto = require("crypto");
|
|
15571
15747
|
import_chalk = __toESM(require_source());
|
|
15572
15748
|
import_ora = __toESM(require_ora());
|
|
15573
15749
|
}
|
|
@@ -15578,7 +15754,7 @@ var require_package = __commonJS({
|
|
|
15578
15754
|
"package.json"(exports2, module2) {
|
|
15579
15755
|
module2.exports = {
|
|
15580
15756
|
name: "slackhive",
|
|
15581
|
-
version: "0.
|
|
15757
|
+
version: "0.5.0",
|
|
15582
15758
|
description: "CLI to install and manage SlackHive \u2014 AI agent teams on Slack",
|
|
15583
15759
|
bin: {
|
|
15584
15760
|
slackhive: "./dist/index.js"
|
|
@@ -15608,7 +15784,7 @@ var require_package = __commonJS({
|
|
|
15608
15784
|
prompts: "^2.4.2"
|
|
15609
15785
|
},
|
|
15610
15786
|
devDependencies: {
|
|
15611
|
-
"@types/node": "^20.
|
|
15787
|
+
"@types/node": "^20.19.41",
|
|
15612
15788
|
"@types/prompts": "^2.4.9",
|
|
15613
15789
|
esbuild: "^0.28.0",
|
|
15614
15790
|
typescript: "^5.3.0"
|
|
@@ -15649,57 +15825,6 @@ var import_chalk2 = __toESM(require_source());
|
|
|
15649
15825
|
var import_ora2 = __toESM(require_ora());
|
|
15650
15826
|
var import_prompts = __toESM(require_prompts3());
|
|
15651
15827
|
var REPO_URL = "https://github.com/pelago-labs/slackhive.git";
|
|
15652
|
-
function parseOAuthFromJson(json) {
|
|
15653
|
-
try {
|
|
15654
|
-
const parsed = JSON.parse(json);
|
|
15655
|
-
const oauth = parsed?.claudeAiOauth;
|
|
15656
|
-
if (oauth?.accessToken && oauth?.refreshToken) {
|
|
15657
|
-
return { accessToken: oauth.accessToken, refreshToken: oauth.refreshToken };
|
|
15658
|
-
}
|
|
15659
|
-
} catch {
|
|
15660
|
-
}
|
|
15661
|
-
return null;
|
|
15662
|
-
}
|
|
15663
|
-
function extractOAuthCredentials() {
|
|
15664
|
-
try {
|
|
15665
|
-
const creds = (0, import_child_process2.execSync)('security find-generic-password -s "Claude Code-credentials" -w', {
|
|
15666
|
-
encoding: "utf-8",
|
|
15667
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
15668
|
-
}).trim();
|
|
15669
|
-
const result = parseOAuthFromJson(creds);
|
|
15670
|
-
if (result) return result;
|
|
15671
|
-
} catch {
|
|
15672
|
-
}
|
|
15673
|
-
try {
|
|
15674
|
-
const creds = (0, import_child_process2.execSync)('secret-tool lookup service "Claude Code-credentials"', {
|
|
15675
|
-
encoding: "utf-8",
|
|
15676
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
15677
|
-
}).trim();
|
|
15678
|
-
const result = parseOAuthFromJson(creds);
|
|
15679
|
-
if (result) return result;
|
|
15680
|
-
} catch {
|
|
15681
|
-
}
|
|
15682
|
-
try {
|
|
15683
|
-
const credPath = (0, import_path2.join)(process.env.HOME || "~", ".claude", ".credentials.json");
|
|
15684
|
-
if ((0, import_fs2.existsSync)(credPath)) {
|
|
15685
|
-
const creds = (0, import_fs2.readFileSync)(credPath, "utf-8").trim();
|
|
15686
|
-
const result = parseOAuthFromJson(creds);
|
|
15687
|
-
if (result) return result;
|
|
15688
|
-
}
|
|
15689
|
-
} catch {
|
|
15690
|
-
}
|
|
15691
|
-
return null;
|
|
15692
|
-
}
|
|
15693
|
-
function syncCredentialsFile(creds) {
|
|
15694
|
-
const credPath = (0, import_path2.join)(process.env.HOME || "~", ".claude", ".credentials.json");
|
|
15695
|
-
const payload = JSON.stringify({
|
|
15696
|
-
claudeAiOauth: {
|
|
15697
|
-
accessToken: creds.accessToken,
|
|
15698
|
-
refreshToken: creds.refreshToken
|
|
15699
|
-
}
|
|
15700
|
-
}, null, 2);
|
|
15701
|
-
(0, import_fs2.writeFileSync)(credPath, payload, { mode: 384 });
|
|
15702
|
-
}
|
|
15703
15828
|
async function init(opts) {
|
|
15704
15829
|
const dir = (0, import_path2.resolve)(opts.dir);
|
|
15705
15830
|
const O = import_chalk2.default.hex("#D97757").bold;
|
|
@@ -15750,75 +15875,20 @@ async function init(opts) {
|
|
|
15750
15875
|
if (freshEnv) {
|
|
15751
15876
|
console.log(import_chalk2.default.bold.hex("#D97757")(" [3/4]") + import_chalk2.default.bold(" Configure environment"));
|
|
15752
15877
|
console.log("");
|
|
15753
|
-
|
|
15754
|
-
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
choices: [
|
|
15758
|
-
{ title: "API Key \u2014 pay-per-use via Anthropic API", value: "apikey" },
|
|
15759
|
-
{ title: "Subscription \u2014 run `claude login` first", value: "subscription" }
|
|
15760
|
-
]
|
|
15761
|
-
});
|
|
15762
|
-
if (!authMode.mode) {
|
|
15763
|
-
console.log(import_chalk2.default.red("\n Setup cancelled."));
|
|
15764
|
-
process.exit(1);
|
|
15765
|
-
}
|
|
15766
|
-
const questions = [];
|
|
15767
|
-
let oauthCreds = null;
|
|
15768
|
-
if (authMode.mode === "apikey") {
|
|
15769
|
-
questions.push({
|
|
15770
|
-
type: "text",
|
|
15771
|
-
name: "anthropicKey",
|
|
15772
|
-
message: "Anthropic API key",
|
|
15773
|
-
validate: (v) => v.startsWith("sk-") ? true : "Must start with sk-"
|
|
15774
|
-
});
|
|
15775
|
-
} else {
|
|
15776
|
-
const claudeDir = (0, import_path2.join)(process.env.HOME || "~", ".claude");
|
|
15777
|
-
if (!(0, import_fs2.existsSync)(claudeDir)) {
|
|
15778
|
-
console.log(import_chalk2.default.yellow("\n warning: ~/.claude not found. Run `claude login` first, then re-run `slackhive init`."));
|
|
15779
|
-
process.exit(1);
|
|
15780
|
-
}
|
|
15781
|
-
console.log(import_chalk2.default.green(" \u2713") + " Found ~/.claude credentials");
|
|
15782
|
-
const spinner = (0, import_ora2.default)(" Extracting OAuth credentials...").start();
|
|
15783
|
-
oauthCreds = extractOAuthCredentials();
|
|
15784
|
-
if (oauthCreds) {
|
|
15785
|
-
spinner.succeed("OAuth credentials extracted");
|
|
15786
|
-
syncCredentialsFile(oauthCreds);
|
|
15787
|
-
} else {
|
|
15788
|
-
spinner.warn("Could not auto-extract credentials from keychain");
|
|
15789
|
-
console.log(import_chalk2.default.gray(" On Linux/headless servers, paste your OAuth token manually."));
|
|
15790
|
-
console.log(import_chalk2.default.gray(" Get it from a machine where you ran `claude login`:"));
|
|
15791
|
-
console.log(import_chalk2.default.gray(' security find-generic-password -s "Claude Code-credentials" -w'));
|
|
15792
|
-
console.log("");
|
|
15793
|
-
const tokenResponse = await (0, import_prompts.default)([
|
|
15794
|
-
{ type: "password", name: "accessToken", message: "OAuth access token (sk-ant-oat01-...)", validate: (v) => v.startsWith("sk-ant-oat") ? true : "Must start with sk-ant-oat" },
|
|
15795
|
-
{ type: "password", name: "refreshToken", message: "OAuth refresh token (sk-ant-ort01-...)", validate: (v) => v.startsWith("sk-ant-ort") ? true : "Must start with sk-ant-ort" }
|
|
15796
|
-
]);
|
|
15797
|
-
if (!tokenResponse.accessToken || !tokenResponse.refreshToken) {
|
|
15798
|
-
console.log(import_chalk2.default.red("\n Setup cancelled. Use API Key mode instead on headless servers."));
|
|
15799
|
-
process.exit(1);
|
|
15800
|
-
}
|
|
15801
|
-
oauthCreds = { accessToken: tokenResponse.accessToken, refreshToken: tokenResponse.refreshToken };
|
|
15802
|
-
syncCredentialsFile(oauthCreds);
|
|
15803
|
-
}
|
|
15804
|
-
}
|
|
15805
|
-
questions.push(
|
|
15878
|
+
console.log(import_chalk2.default.gray(" Agent backend & credentials (Claude Code or Codex) are configured"));
|
|
15879
|
+
console.log(import_chalk2.default.gray(" in the web UI: Settings \u2192 Agent Backend, after first launch."));
|
|
15880
|
+
console.log("");
|
|
15881
|
+
const response = await (0, import_prompts.default)([
|
|
15806
15882
|
{ type: "text", name: "adminUsername", message: "Admin username", initial: "admin" },
|
|
15807
15883
|
{ type: "password", name: "adminPassword", message: "Admin password", validate: (v) => v.length >= 6 ? true : "At least 6 characters" }
|
|
15808
|
-
);
|
|
15809
|
-
const response = await (0, import_prompts.default)(questions);
|
|
15884
|
+
]);
|
|
15810
15885
|
if (!response.adminPassword) {
|
|
15811
15886
|
console.log(import_chalk2.default.red("\n Setup cancelled."));
|
|
15812
15887
|
process.exit(1);
|
|
15813
15888
|
}
|
|
15814
15889
|
let envContent = "# Generated by slackhive init\n\n";
|
|
15815
|
-
|
|
15816
|
-
envContent += `ANTHROPIC_API_KEY=${response.anthropicKey}
|
|
15817
|
-
`;
|
|
15818
|
-
} else {
|
|
15819
|
-
envContent += `# Claude Code subscription \u2014 auth handled by system (claude login)
|
|
15890
|
+
envContent += `# Agent backend auth (Claude Code / Codex) is configured in Settings \u2192 Agent Backend
|
|
15820
15891
|
`;
|
|
15821
|
-
}
|
|
15822
15892
|
envContent += `
|
|
15823
15893
|
# SQLite, no Docker required
|
|
15824
15894
|
`;
|
|
@@ -15858,20 +15928,8 @@ NODE_ENV=production
|
|
|
15858
15928
|
envContents = (0, import_fs2.readFileSync)(envPath, "utf-8");
|
|
15859
15929
|
console.log(import_chalk2.default.green(" \u2713") + " .env patched");
|
|
15860
15930
|
}
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
const freshCreds = extractOAuthCredentials();
|
|
15864
|
-
if (freshCreds) {
|
|
15865
|
-
envContents = envContents.replace(/CLAUDE_CODE_OAUTH_TOKEN=.*/, `CLAUDE_CODE_OAUTH_TOKEN=${freshCreds.accessToken}`).replace(/CLAUDE_CODE_OAUTH_REFRESH_TOKEN=.*/, `CLAUDE_CODE_OAUTH_REFRESH_TOKEN=${freshCreds.refreshToken}`);
|
|
15866
|
-
(0, import_fs2.writeFileSync)(envPath, envContents);
|
|
15867
|
-
syncCredentialsFile(freshCreds);
|
|
15868
|
-
spinner.succeed("OAuth credentials synced");
|
|
15869
|
-
} else {
|
|
15870
|
-
spinner.warn("Could not extract credentials \u2014 run `claude login` if agents fail to authenticate");
|
|
15871
|
-
}
|
|
15872
|
-
} else {
|
|
15873
|
-
console.log(import_chalk2.default.yellow(" note: .env already exists \u2014 skipping configuration"));
|
|
15874
|
-
}
|
|
15931
|
+
console.log(import_chalk2.default.yellow(" note: .env already exists \u2014 skipping configuration"));
|
|
15932
|
+
console.log(import_chalk2.default.gray(" Agent backend auth (Claude Code or Codex) is managed in Settings \u2192 Agent Backend."));
|
|
15875
15933
|
console.log("");
|
|
15876
15934
|
}
|
|
15877
15935
|
let webReady = true;
|
|
@@ -15981,6 +16039,8 @@ program2.command("stop").description("Stop all SlackHive services (also kills or
|
|
|
15981
16039
|
program2.command("status").description("Show running SlackHive containers").action(status);
|
|
15982
16040
|
program2.command("logs").description("Tail runner service logs").option("-f, --follow", "Follow log output", true).action(logs);
|
|
15983
16041
|
program2.command("update").description("Pull latest changes and rebuild").action(update);
|
|
16042
|
+
program2.command("backup").description("Create a database backup (consistent snapshot)").option("-o, --output <path>", "Write the backup to this path instead of ~/.slackhive/backups/").action(backup);
|
|
16043
|
+
program2.command("restore").description("Restore the database from a backup (stops the runner; the only restore path)").requiredOption("-f, --from <file>", "Backup .db file to restore").option("--recovery-key <file>", "Password-wrapped recovery-key file (prompts for password; set SLACKHIVE_RECOVERY_PASSWORD to skip the prompt)").option("--env <file>", "Plain .env file to install instead of a recovery key").action(restore);
|
|
15984
16044
|
program2.parse(process.argv);
|
|
15985
16045
|
/*! Bundled license information:
|
|
15986
16046
|
|
package/package.json
CHANGED
|
@@ -1,45 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slackhive",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Open-source platform for managing teams of Claude Code Slack agents with a boss agent orchestrator",
|
|
6
|
+
"workspaces": [
|
|
7
|
+
"apps/*",
|
|
8
|
+
"packages/*",
|
|
9
|
+
"cli"
|
|
10
|
+
],
|
|
5
11
|
"bin": {
|
|
6
|
-
"slackhive": "
|
|
12
|
+
"slackhive": "cli/dist/index.js"
|
|
7
13
|
},
|
|
8
14
|
"scripts": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
15
|
+
"dev": "sh scripts/dev.sh",
|
|
16
|
+
"build": "npm run build -w packages/shared && npm run build --workspaces --if-present",
|
|
17
|
+
"prepare": "npm run build -w cli",
|
|
18
|
+
"postinstall": "node scripts/postinstall.js"
|
|
11
19
|
},
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"claude",
|
|
17
|
-
"claude-code",
|
|
18
|
-
"slackhive"
|
|
20
|
+
"files": [
|
|
21
|
+
"cli/dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"scripts/postinstall.js"
|
|
19
24
|
],
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"repository": {
|
|
23
|
-
"type": "git",
|
|
24
|
-
"url": "https://github.com/pelago-labs/slackhive"
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=20.0.0"
|
|
25
27
|
},
|
|
28
|
+
"license": "MIT",
|
|
26
29
|
"dependencies": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
30
|
+
"@anthropic-ai/claude-agent-sdk": "0.2.118",
|
|
31
|
+
"@anthropic-ai/claude-code": "^2.1.118",
|
|
32
|
+
"@elastic/elasticsearch": "^9.3.4",
|
|
33
|
+
"@mozilla/readability": "^0.6.0",
|
|
34
|
+
"linkedom": "^0.18.12",
|
|
35
|
+
"pg": "^8.20.0",
|
|
36
|
+
"turndown": "^7.2.4"
|
|
31
37
|
},
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
38
|
+
"overrides": {
|
|
39
|
+
"dompurify": "^3.4.0",
|
|
40
|
+
"vite": "^6.4.3",
|
|
41
|
+
"axios": "^1.16.0",
|
|
42
|
+
"hono": "^4.12.25",
|
|
43
|
+
"@hono/node-server": "^1.19.13",
|
|
44
|
+
"follow-redirects": "^1.15.12",
|
|
45
|
+
"fast-uri": "^3.1.2",
|
|
46
|
+
"ip-address": "^10.2.0",
|
|
47
|
+
"express-rate-limit": "^8.5.1",
|
|
48
|
+
"form-data": "^4.0.6",
|
|
49
|
+
"undici": "^7.28.0",
|
|
50
|
+
"ws": "^8.21.0"
|
|
40
51
|
},
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
]
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/turndown": "^5.0.6"
|
|
54
|
+
}
|
|
45
55
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Remove the musl Claude binary on glibc Linux systems.
|
|
3
|
+
// When npm install is run with --legacy-peer-deps, npm ignores the
|
|
4
|
+
// libc:["musl"] constraint on @anthropic-ai/claude-agent-sdk-linux-x64-musl
|
|
5
|
+
// and downloads the binary anyway. The claude-agent-sdk then picks musl first
|
|
6
|
+
// (before glibc) and fails to spawn it because the musl dynamic linker is
|
|
7
|
+
// absent on glibc systems. Removing the binary causes require.resolve to throw
|
|
8
|
+
// for the musl path, so the SDK falls back to the glibc binary.
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
if (process.platform !== 'linux') process.exit(0);
|
|
14
|
+
|
|
15
|
+
const muslBinary = path.join(__dirname, '..', 'node_modules', '@anthropic-ai', 'claude-agent-sdk-linux-x64-musl', 'claude');
|
|
16
|
+
if (!fs.existsSync(muslBinary)) process.exit(0);
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const ldd = execSync('ldd --version 2>&1').toString();
|
|
20
|
+
if (!ldd.includes('musl')) {
|
|
21
|
+
fs.unlinkSync(muslBinary);
|
|
22
|
+
console.log('postinstall: removed musl Claude binary (glibc system detected)');
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// ldd not available or failed — leave binary as-is
|
|
26
|
+
}
|