fix: delete key generation commands and fixing session ID

This commit is contained in:
ajspig 2026-04-13 13:13:27 -04:00
parent 8b32aa4622
commit 457c972a0f
7 changed files with 13 additions and 187 deletions

View File

@ -101,12 +101,6 @@ honcho # show banner + command list
| `honcho conclusion create` | Create a conclusion |
| `honcho conclusion delete <id>` | Delete a conclusion |
### Keys
| Command | Description |
|---------|-------------|
| `honcho key generate` | Generate a scoped JWT (workspace/peer/session) |
### Config
| Command | Description |

View File

@ -1,119 +0,0 @@
"""Key management commands: generate scoped JWTs."""
from __future__ import annotations
import re
from datetime import datetime, timedelta, timezone
from typing import Optional
import typer
from honcho_cli.output import print_error, print_result, status
from honcho_cli.common import add_common_options
app = typer.Typer(help="JWT key management.")
add_common_options(app)
def _parse_duration(duration: str) -> datetime:
"""Parse duration string like '30d', '24h', '90d' into expiry datetime."""
match = re.match(r"^(\d+)([dhm])$", duration)
if not match:
print_error(
"INVALID_DURATION",
f"Invalid duration format: '{duration}'. Use format like '30d', '24h', '60m'.",
{"duration": duration},
)
raise typer.Exit(1)
value = int(match.group(1))
unit = match.group(2)
delta = {
"d": timedelta(days=value),
"h": timedelta(hours=value),
"m": timedelta(minutes=value),
}[unit]
return datetime.now(timezone.utc) + delta
@app.command()
def generate(
workspace: Optional[str] = typer.Option(None, "--workspace", help="Scope to workspace"),
peer: Optional[str] = typer.Option(None, "--peer", help="Scope to peer"),
session: Optional[str] = typer.Option(None, "--session", help="Scope to session"),
admin: bool = typer.Option(False, "--admin", help="Generate admin key"),
expires: str = typer.Option("90d", "--expires", help="Expiry duration (e.g., 30d, 24h)"),
no_expire: bool = typer.Option(False, "--no-expire", help="No expiration (use with caution)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Generate a scoped JWT key.
Requires an admin JWT in config. Generated keys are scoped down from admin.
"""
from honcho_cli.common import handle_cmd_flags
from honcho_cli.main import get_resolved_config
handle_cmd_flags(json_output=json_output)
config = get_resolved_config()
if not config.api_key:
print_error("NO_API_KEY", "Admin API key required in config. Run `honcho config set api_key <key>`.")
raise typer.Exit(3)
if not admin and not workspace and not peer and not session:
# Default to current workspace
workspace = config.workspace_id
if not workspace:
print_error(
"NO_SCOPE",
"Must specify at least one scope: --workspace, --peer, --session, or --admin",
)
raise typer.Exit(1)
# Build expiry
expires_at = None
if not no_expire:
expires_at = _parse_duration(expires)
# Use raw HTTP to call the /keys endpoint
import httpx
base_url = config.base_url.rstrip("/")
params: dict = {}
if workspace:
params["workspace_id"] = workspace
if peer:
params["peer_id"] = peer
if session:
params["session_id"] = session
if expires_at:
params["expires_at"] = expires_at.isoformat()
if admin:
params = {"admin": "true"}
if expires_at:
params["expires_at"] = expires_at.isoformat()
try:
resp = httpx.post(
f"{base_url}/v3/keys",
params=params,
headers={"Authorization": f"Bearer {config.api_key}"},
timeout=30,
)
resp.raise_for_status()
data = resp.json()
print_result(data)
except httpx.HTTPStatusError as e:
if e.response.status_code == 401 or e.response.status_code == 403:
print_error("AUTH_ERROR", "Admin authentication required for key generation", {})
raise typer.Exit(3)
print_error("KEY_ERROR", f"Failed to generate key: {e.response.text}", {})
raise typer.Exit(1)
except Exception as e:
print_error("KEY_ERROR", f"Failed to generate key: {e}", {})
raise typer.Exit(1)

View File

@ -123,11 +123,10 @@ def card(
@app.command()
def chat(
query: str = typer.Argument(help="Question to ask about the peer"),
peer_id: Optional[str] = typer.Option(None, help="Peer ID (uses default if omitted)"),
target: Optional[str] = typer.Option(None, help="Target peer for perspective"),
session: Optional[str] = typer.Option(None, help="Session context"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Query the dialectic about a peer."""
@ -135,7 +134,7 @@ def chat(
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
pid = _get_peer_id(None)
client, config = get_client()
p = client.peer(pid)
@ -149,10 +148,9 @@ def chat(
@app.command()
def search(
query: str = typer.Argument(help="Search query"),
peer_id: Optional[str] = typer.Option(None, help="Peer ID (uses default if omitted)"),
limit: int = typer.Option(10, help="Max results"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Search a peer's messages."""
@ -160,7 +158,7 @@ def search(
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
pid = _get_peer_id(None)
client, config = get_client()
p = client.peer(pid)
@ -245,10 +243,9 @@ def get_metadata(
@app.command("set-metadata")
def set_metadata(
peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
metadata: str = typer.Option(..., "--metadata", "-m", help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
metadata: str = typer.Argument(help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Set metadata for a peer."""
@ -256,7 +253,7 @@ def set_metadata(
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
pid = _get_peer_id(peer_id)
pid = _get_peer_id(None)
client, config = get_client()
try:

View File

@ -302,10 +302,9 @@ def remove_peers(
@app.command()
def search(
query: str = typer.Argument(help="Search query"),
session_id: Optional[str] = typer.Option(None, help="Session ID (uses default if omitted)"),
limit: int = typer.Option(10, help="Max results"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID (uses default if omitted)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Search messages in a session."""
@ -313,7 +312,7 @@ def search(
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
sid = _get_session_id(None)
client, config = get_client()
sess = client.session(sid)
@ -336,12 +335,11 @@ def search(
@app.command()
def representation(
peer_id: str = typer.Argument(help="Peer ID to get representation for"),
session_id: Optional[str] = typer.Option(None, help="Session ID (uses default if omitted)"),
target: Optional[str] = typer.Option(None, help="Target peer (what peer_id knows about target)"),
search_query: Optional[str] = typer.Option(None, help="Semantic search query to filter conclusions"),
max_conclusions: Optional[int] = typer.Option(None, help="Maximum number of conclusions to include"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID (uses default if omitted)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Get the representation of a peer within a session."""
@ -349,7 +347,7 @@ def representation(
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
sid = _get_session_id(None)
client, config = get_client()
sess = client.session(sid)
@ -391,9 +389,8 @@ def get_metadata(
@app.command("set-metadata")
def set_metadata(
metadata: str = typer.Argument(help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
session_id: Optional[str] = typer.Option(None, help="Session ID (uses default if omitted)"),
workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
session: Optional[str] = typer.Option(None, "--session", "-s", help="Session ID (uses default if omitted)"),
json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
"""Set metadata for a session."""
@ -401,7 +398,7 @@ def set_metadata(
from honcho_cli.main import get_client
handle_cmd_flags(json_output=json_output, workspace=workspace, session=session)
sid = _get_session_id(session_id)
sid = _get_session_id(None)
client, config = get_client()
try:

View File

@ -108,7 +108,6 @@ app.command()(doctor)
# Register command groups
from honcho_cli.commands.config_cmd import app as config_app
from honcho_cli.commands.conclusion import app as conclusion_app
from honcho_cli.commands.key import app as key_app
from honcho_cli.commands.message import app as message_app
from honcho_cli.commands.peer import app as peer_app
from honcho_cli.commands.session import app as session_app
@ -120,7 +119,6 @@ app.add_typer(peer_app, name="peer")
app.add_typer(session_app, name="session")
app.add_typer(message_app, name="message")
app.add_typer(conclusion_app, name="conclusion")
app.add_typer(key_app, name="key")
if __name__ == "__main__":

View File

@ -41,4 +41,3 @@ honcho config set peer_id my-peer
- `honcho session` — Inspect, messages, context, summaries
- `honcho message` — List and get messages
- `honcho conclusion` — List, search, create, delete conclusions
- `honcho key` — Generate scoped JWT keys

View File

@ -1,40 +0,0 @@
---
name: honcho-cli-admin
version: 0.1.0
description: Admin operations for Honcho workspaces
---
# Honcho CLI — Admin Skills
## Rules
- Always inspect before deleting: `honcho workspace inspect` then `honcho workspace delete`
- Use `--dry-run` for destructive operations to preview impact
- Generated keys default to 90-day expiry; use `--no-expire` only when intentional
- Admin JWT required for key generation and workspace deletion
## Key Generation
```bash
# Workspace-scoped key (90-day default)
honcho key generate --workspace my-ws --json
# Peer-scoped key with custom expiry
honcho key generate --peer <id> --expires 30d --json
# No-expiry key (use with caution)
honcho key generate --workspace my-ws --no-expire --json
```
## Workspace Lifecycle
```bash
# Inspect first
honcho workspace inspect <workspace_id> --json
# Check queue status
honcho workspace queue-status --json
# Delete (requires --yes for non-interactive)
honcho workspace delete <workspace_id> --yes
```