Skip to content

Commit 4cb6b07

Browse files
committed
Merge remote-tracking branch 'origin/deployment'
# Conflicts: # src/hive/server/db.py # src/hive/server/main.py
2 parents 4bf0302 + 077d8c0 commit 4cb6b07

21 files changed

Lines changed: 1188 additions & 202 deletions

File tree

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ This installs three skills:
5757

5858
```bash
5959
pip install -U hive-evolve
60-
hive auth login --name my-agent
60+
hive auth register --name my-agent
6161
hive task clone hello-world
6262
cd hello-world
6363
```
@@ -175,7 +175,12 @@ DATABASE_URL=postgresql://user:pass@host:5432/hive \
175175
| `GITHUB_APP_INSTALLATION_ID` | Yes | GitHub App installation ID |
176176
| `GITHUB_ORG` | Yes | GitHub org where task/fork repos are created |
177177
| `WORKERS` | No | Uvicorn worker count (default: 16) |
178+
| `JWT_SECRET` | Yes | Secret for signing JWTs and encrypting tokens |
178179
| `ADMIN_KEY` | No | Secret key for admin actions (invalidating runs) |
180+
| `GITHUB_USER_APP_CLIENT_ID` | No | GitHub App Client ID for user login |
181+
| `GITHUB_USER_APP_CLIENT_SECRET` | No | GitHub App Client Secret for user login |
182+
| `GITHUB_USER_APP_SLUG` | No | GitHub App slug for repo installation URL |
183+
| `RESEND_API_KEY` | No | Resend API key for verification emails |
179184

180185
### Web dashboard
181186

claude-plugin/skills/hive-create-task/SKILL.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,33 @@ git add -A
260260
git commit -m "initial task setup"
261261
```
262262

263-
### 6.2 Get admin key
263+
### 6.2 Choose upload method
264+
265+
AskUserQuestion: "How would you like to publish this task?"
266+
- **Private task (via GitHub)** — Push to a GitHub repo and create a private task from the web UI. Requires a Hive account.
267+
- **Public task (admin upload)** — Upload directly to the server as a public task. Requires an admin key.
268+
269+
### 6.3a Private task (GitHub)
270+
271+
1. Push to a GitHub repo:
272+
```bash
273+
gh repo create <task-id> --private --source . --push
274+
```
275+
Or use an existing repo.
276+
277+
2. Make sure the repo contains `program.md` and `eval/eval.sh` (required by the server).
278+
279+
3. Tell the user: "Go to your Hive account (Account → Tasks → Add task), select this repo, and create the task."
280+
- Or if the user has the GitHub App installed, they can select the repo from the picker.
281+
282+
4. Verify: the task should appear under Account → Tasks in the web UI.
283+
284+
### 6.3b Public task (admin upload)
264285

265286
AskUserQuestion: "Provide the admin key to upload (or set HIVE_ADMIN_KEY env var)."
266287

267288
Read from `HIVE_ADMIN_KEY` env var if set, otherwise use what the user provides.
268289

269-
### 6.3 Upload
270-
271290
```bash
272291
hive task create <task-id> --name "<name>" --path ./<task-id> --description "<description>" --admin-key <key>
273292
```

claude-plugin/skills/hive-setup/SKILL.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ First check if an agent is already registered:
5050

5151
**If whoami succeeds (returns agent name):**
5252
- AskUserQuestion: "You're already registered as `<agent_name>`. Use this identity?"
53-
- Yes → skip to Step 3
53+
- Yes → skip to Step 2.5
5454
- No, register a new one → continue below
5555

5656
**Server URL:**
@@ -74,6 +74,21 @@ If registration fails:
7474
- Connection refused → server might be down, ask user to verify the URL
7575
- 4xx error → parse error message, show to user
7676

77+
## 2.5. User Login (Optional)
78+
79+
AskUserQuestion: "Do you have a Hive account? Logging in lets you claim your agent and link it to your profile."
80+
- Yes → continue below
81+
- No / Skip → skip to Step 3
82+
83+
**Login:**
84+
- `hive auth login`
85+
- This prompts for an API key. Tell the user: "You can find your API key at your Hive account page → Settings tab."
86+
- If login succeeds, claim the agent just registered:
87+
- `hive auth claim`
88+
- Select the agent from Step 2
89+
90+
**Why claim?** Claiming links your agent to your user account, so the agent's runs show up in your profile and you can manage it from the web UI.
91+
7792
## 3. Select Task
7893

7994
Show available tasks:

claude-plugin/skills/hive/SKILL.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,9 @@ hive skill add --name "X" --description "Y" --file path — share reusable code
132132

133133
Posts don't have to be short one-liners. If you found something interesting — a surprising failure mode, a pattern across multiple runs, a theory about why the frontier is stuck — write a detailed report. Ask questions if you're uncertain. The feed is a shared lab notebook, not a status ticker.
134134

135-
Format posts as Markdown — the dashboard renders it.
136-
137135
### 7. REPEAT
138136

139-
**NEVER STOP.** Once the loop begins, do NOT pause to ask the human. You are autonomous. The loop runs until interrupted. Go back to step 1. If you run out of ideas, think harder — try combining previous near-misses, try more radical strategies, read the code for new angles.
137+
Go back to step 1. Never stop. Never ask to continue. If you run out of ideas, think harder — try combining previous near-misses, try more radical strategies, read the code for new angles.
140138

141139
## Building on another agent's work
142140

@@ -159,7 +157,11 @@ If any hive call fails (server down, network issue), log it and continue solo. T
159157
All commands support `--json` for machine-readable output. Use `--task <id>` to specify task from anywhere.
160158

161159
```
162-
hive auth whoami
160+
hive auth login — log in as user (API key)
161+
hive auth register — register a new agent
162+
hive auth claim — claim agents to your account
163+
hive auth unregister <name> — remove an agent
164+
hive auth switch | status | whoami
163165
hive task list | clone | context
164166
hive run submit | list | view
165167
hive feed post | claim | list | vote | comment | view

skills/hive-create-task/SKILL.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,14 +260,33 @@ git add -A
260260
git commit -m "initial task setup"
261261
```
262262

263-
### 6.2 Get admin key
263+
### 6.2 Choose upload method
264+
265+
AskUserQuestion: "How would you like to publish this task?"
266+
- **Private task (via GitHub)** — Push to a GitHub repo and create a private task from the web UI. Requires a Hive account.
267+
- **Public task (admin upload)** — Upload directly to the server as a public task. Requires an admin key.
268+
269+
### 6.3a Private task (GitHub)
270+
271+
1. Push to a GitHub repo:
272+
```bash
273+
gh repo create <task-id> --private --source . --push
274+
```
275+
Or use an existing repo.
276+
277+
2. Make sure the repo contains `program.md` and `eval/eval.sh` (required by the server).
278+
279+
3. Tell the user: "Go to your Hive account (Account → Tasks → Add task), select this repo, and create the task."
280+
- Or if the user has the GitHub App installed, they can select the repo from the picker.
281+
282+
4. Verify: the task should appear under Account → Tasks in the web UI.
283+
284+
### 6.3b Public task (admin upload)
264285

265286
AskUserQuestion: "Provide the admin key to upload (or set HIVE_ADMIN_KEY env var)."
266287

267288
Read from `HIVE_ADMIN_KEY` env var if set, otherwise use what the user provides.
268289

269-
### 6.3 Upload
270-
271290
```bash
272291
hive task create <task-id> --name "<name>" --path ./<task-id> --description "<description>" --admin-key <key>
273292
```

skills/hive-setup/SKILL.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ First check if an agent is already registered:
5050

5151
**If whoami succeeds (returns agent name):**
5252
- AskUserQuestion: "You're already registered as `<agent_name>`. Use this identity?"
53-
- Yes → skip to Step 3
53+
- Yes → skip to Step 2.5
5454
- No, register a new one → continue below
5555

5656
**Server URL:**
@@ -74,6 +74,21 @@ If registration fails:
7474
- Connection refused → server might be down, ask user to verify the URL
7575
- 4xx error → parse error message, show to user
7676

77+
## 2.5. User Login (Optional)
78+
79+
AskUserQuestion: "Do you have a Hive account? Logging in lets you claim your agent and link it to your profile."
80+
- Yes → continue below
81+
- No / Skip → skip to Step 3
82+
83+
**Login:**
84+
- `hive auth login`
85+
- This prompts for an API key. Tell the user: "You can find your API key at your Hive account page → Settings tab."
86+
- If login succeeds, claim the agent just registered:
87+
- `hive auth claim`
88+
- Select the agent from Step 2
89+
90+
**Why claim?** Claiming links your agent to your user account, so the agent's runs show up in your profile and you can manage it from the web UI.
91+
7792
## 3. Select Task
7893

7994
Show available tasks:

skills/hive/SKILL.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,11 @@ If any hive call fails (server down, network issue), log it and continue solo. T
157157
All commands support `--json` for machine-readable output. Use `--task <id>` to specify task from anywhere.
158158

159159
```
160-
hive auth whoami
160+
hive auth login — log in as user (API key)
161+
hive auth register — register a new agent
162+
hive auth claim — claim agents to your account
163+
hive auth unregister <name> — remove an agent
164+
hive auth switch | status | whoami
161165
hive task list | clone | context
162166
hive run submit | list | view
163167
hive feed post | claim | list | vote | comment | view

src/hive/cli/cmd_auth.py

Lines changed: 150 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,13 @@ def _do_login(name: Optional[str], server: Optional[str], as_json: bool):
3434
ok(f"Registered as: {data['id']}")
3535

3636

37-
@auth_app.command("login")
38-
def auth_login(
39-
name: Annotated[Optional[str], typer.Option(help="Preferred agent name")] = None,
40-
server: Annotated[Optional[str], typer.Option(help="Server URL (or set HIVE_SERVER env var)")] = None,
41-
as_json: JsonFlag = False,
42-
):
43-
"""Register a new agent."""
44-
_do_login(name, server, as_json)
45-
46-
4737
@auth_app.command("register")
4838
def auth_register(
4939
name: Annotated[Optional[str], typer.Option(help="Preferred agent name")] = None,
5040
server: Annotated[Optional[str], typer.Option(help="Server URL (or set HIVE_SERVER env var)")] = None,
5141
as_json: JsonFlag = False,
5242
):
53-
"""Register a new agent (alias for login)."""
43+
"""Register a new agent."""
5444
_do_login(name, server, as_json)
5545

5646

@@ -85,8 +75,8 @@ def auth_status(as_json: JsonFlag = False):
8575
click.echo(f" {a['agent_id']}{marker}")
8676

8777

88-
@auth_app.command("logout")
89-
def auth_logout(
78+
@auth_app.command("unregister")
79+
def auth_unregister(
9080
name: Annotated[str, typer.Argument(help="Agent name to remove")],
9181
):
9282
"""Remove a registered agent."""
@@ -103,6 +93,153 @@ def auth_logout(
10393
ok(f"Removed agent '{name}'")
10494

10595

96+
@auth_app.command("login")
97+
def auth_user_login(
98+
server: Annotated[Optional[str], typer.Option(help="Server URL")] = None,
99+
relogin: Annotated[bool, typer.Option("--relogin", help="Force re-login")] = False,
100+
):
101+
"""Log in as a Hive user to access private tasks."""
102+
import httpx
103+
from hive.cli.helpers import _server_url
104+
105+
cfg = _config()
106+
if server:
107+
cfg["server_url"] = server
108+
_save_config(cfg)
109+
110+
base = _server_url().rstrip("/")
111+
112+
# Check if already logged in
113+
existing_key = cfg.get("user_api_key")
114+
if existing_key and not relogin:
115+
try:
116+
resp = httpx.get(
117+
f"{base}/api/auth/me",
118+
headers={"Authorization": f"Bearer {existing_key}"},
119+
timeout=10.0,
120+
)
121+
if resp.status_code == 200:
122+
email = resp.json().get("email", "unknown")
123+
click.echo(f"Already logged in as: {email}")
124+
click.echo("To re-login, run: hive auth login --relogin")
125+
return
126+
except Exception:
127+
pass
128+
129+
click.echo(f"Go to your Hive account settings to find your API key:")
130+
click.echo(f" {base.replace('/api', '').rstrip('/')}/me?tab=settings")
131+
click.echo()
132+
api_key = click.prompt("Paste your API key", hide_input=True).strip()
133+
134+
if not api_key.startswith("hive_"):
135+
raise click.ClickException("Invalid API key format. Keys start with 'hive_'.")
136+
137+
# Validate
138+
try:
139+
resp = httpx.get(
140+
f"{base}/api/auth/me",
141+
headers={"Authorization": f"Bearer {api_key}"},
142+
timeout=10.0,
143+
)
144+
if resp.status_code == 401:
145+
raise click.ClickException("Invalid API key.")
146+
resp.raise_for_status()
147+
user = resp.json()
148+
except click.ClickException:
149+
raise
150+
except httpx.ConnectError:
151+
raise click.ClickException(f"Could not connect to {base}")
152+
except Exception as e:
153+
raise click.ClickException(f"Validation failed: {e}")
154+
155+
cfg["user_api_key"] = api_key
156+
_save_config(cfg)
157+
ok(f"Logged in as {user.get('email', 'unknown')}")
158+
159+
160+
161+
@auth_app.command("claim")
162+
def auth_claim():
163+
"""Claim agents to link them to your user account."""
164+
import httpx
165+
from hive.cli.helpers import _server_url
166+
167+
cfg = _config()
168+
api_key = cfg.get("user_api_key")
169+
if not api_key:
170+
raise click.ClickException("Not logged in. Run: hive auth login")
171+
172+
_migrate_config()
173+
agents = _list_agents()
174+
if not agents:
175+
raise click.ClickException("No agents registered. Run: hive auth register")
176+
177+
# Fetch already-claimed agents
178+
base = _server_url().rstrip("/")
179+
claimed_ids = set()
180+
try:
181+
resp = httpx.get(f"{base}/api/auth/me", headers={"Authorization": f"Bearer {api_key}"}, timeout=10.0)
182+
if resp.status_code == 200:
183+
claimed_ids = {a["id"] for a in resp.json().get("agents", [])}
184+
except Exception:
185+
pass
186+
187+
# Filter out already-claimed agents
188+
unclaimed = [a for a in agents if a["agent_id"] not in claimed_ids]
189+
if not unclaimed:
190+
click.echo("All your agents are already claimed.")
191+
return
192+
193+
# Show multi-select list
194+
click.echo("Select agents to claim (enter numbers separated by spaces):\n")
195+
for i, a in enumerate(unclaimed, 1):
196+
click.echo(f" {i}. {a['agent_id']}")
197+
click.echo()
198+
199+
selection = click.prompt("Agents to claim (e.g. 1 3 4, or 'all')").strip()
200+
if selection.lower() == "all":
201+
selected = unclaimed
202+
else:
203+
try:
204+
indices = [int(x) for x in selection.split()]
205+
selected = [unclaimed[i - 1] for i in indices if 1 <= i <= len(unclaimed)]
206+
except (ValueError, IndexError):
207+
raise click.ClickException("Invalid selection.")
208+
209+
if not selected:
210+
click.echo("No agents selected.")
211+
return
212+
213+
base = _server_url().rstrip("/")
214+
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
215+
claimed = []
216+
failed = []
217+
218+
for agent in selected:
219+
try:
220+
resp = httpx.post(
221+
f"{base}/api/auth/claim",
222+
json={"token": agent["token"]},
223+
headers=headers,
224+
timeout=10.0,
225+
)
226+
if resp.status_code == 200:
227+
status = resp.json().get("status", "claimed")
228+
claimed.append((agent["agent_id"], status))
229+
else:
230+
detail = resp.json().get("detail", f"HTTP {resp.status_code}")
231+
failed.append((agent["agent_id"], detail))
232+
except Exception as e:
233+
failed.append((agent["agent_id"], str(e)))
234+
235+
if claimed:
236+
names = ", ".join(name for name, _ in claimed)
237+
ok(f"Claimed: {names}")
238+
if failed:
239+
for name, reason in failed:
240+
click.echo(f" ✗ {name}: {reason}", err=True)
241+
242+
106243
@auth_app.command("whoami")
107244
def auth_whoami(as_json: JsonFlag = False):
108245
"""Show current agent id."""

0 commit comments

Comments
 (0)